インスタンス状態の保存を使用してアクティビティ状態を保存する方法は?


2620

私はAndroid SDKプラットフォームに取り組んできましたが、アプリケーションの状態を保存する方法は少し不明確です。したがって、「Hello、Android」の例のこのマイナーなリツールを考えると:

package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {

  private TextView mTextView = null;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    if (savedInstanceState == null) {
       mTextView.setText("Welcome to HelloAndroid!");
    } else {
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}

最も単純なケースではそれで十分だと思いましたが、アプリからどのように移動しても、常に最初のメッセージで応答します。

解決策はオーバーライドonPauseなどの単純なものであると確信していますが、ドキュメントを30分ほど探し続けていて、明らかなものは何も見つかりませんでした。


9
いつsavedInstanceState == nullで、いつnullでないのですか?
Trojan.ZBOT 2013

90
あなたは明示的にあなたの活動を破壊している-あなたが言ったように、戻るなどによって活動から離れてナビゲートする。実際、この「savedInstanceState」が使用されるシナリオは、Androidがレクリエーションのためにアクティビティを破棄する場合です。一応:アクティビティの実行中に携帯電話の言語を変更した場合(プロジェクトから別のリソースをロードする必要があるため)。もう1つの非常に一般的なシナリオは、アクティビティを再作成して横向きに表示するために、スマートフォンを横に回転させる場合です。
villoren 2014年

16
2番目のメッセージを取得するには、開発オプションで「アクティビティを保持しない」を有効にします。ホームボタンを押して、最近から戻ってください。
Yaroslav Mytkalyk 2014


6
あなたはそれを行うことができます:onSaveInstanceState(Bundle savedInstanceState)
VahidHoseini

回答:


2568

onSaveInstanceState(Bundle savedInstanceState)変更するアプリケーションの状態値をオーバーライドして、次のBundleようにパラメーターに書き込む必要があります。

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  // Save UI state changes to the savedInstanceState.
  // This bundle will be passed to onCreate if the process is
  // killed and restarted.
  savedInstanceState.putBoolean("MyBoolean", true);
  savedInstanceState.putDouble("myDouble", 1.9);
  savedInstanceState.putInt("MyInt", 1);
  savedInstanceState.putString("MyString", "Welcome back to Android");
  // etc.
}

バンドルは、基本的にNVP(「名前と値のペア」)マップを格納する方法であり、それはに渡されますonCreate()し、またonRestoreInstanceState()、あなたがこのような活動から値を抽出する場所:

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  // Restore UI state from the savedInstanceState.
  // This bundle has also been passed to onCreate.
  boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
  double myDouble = savedInstanceState.getDouble("myDouble");
  int myInt = savedInstanceState.getInt("MyInt");
  String myString = savedInstanceState.getString("MyString");
}

またはフラグメントから。

@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
    super.onViewStateRestored(savedInstanceState);
    // Restore UI state from the savedInstanceState.
    // This bundle has also been passed to onCreate.
    boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
    double myDouble = savedInstanceState.getDouble("myDouble");
    int myInt = savedInstanceState.getInt("MyInt");
    String myString = savedInstanceState.getString("MyString");
}

通常、この手法を使用して、アプリケーションのインスタンス値(選択、未保存のテキストなど)を格納します。


24
これは電話では機能するが、エミュレータでは機能しない可能性はありますか?null以外のsavedInstanceStateを取得できないようです。
アダムジャック

491
注意:バンドルに値を追加する前にsuper.onSaveInstanceState(savedInstanceState)を呼び出す必要があります。そうしないと、その呼び出しで値が消去されます(Droid X And​​roid 2.2)。
jkschneider 2011

121
注意:onsaveinstance-methodはAndroidライフサイクルの一部ではないため、公式ドキュメントにはonPause-Method内に重要な情報を保存する必要があると記載されています。developer.android.com/reference/android/app/Activity.html
schlingel

32
そのonSaveInstanceStateため、画面の向きが変わった場合を除いて、事実上ほとんど役に立たなくなります。他のほとんどすべての場合、これに依存することはできず、UI状態を別の場所に手動で保存する必要があります。または、[戻る]ボタンの動作をオーバーライドして、アプリが強制終了されるのを防ぎます。そもそもなぜこんなふうに実装したのかよくわかりません。完全に直感的ではありません。また、この非常に特殊な方法を除いて、システムにバンドルして物事を保存することはできません。
chakrit 2013

12
バンドルへの/からのUI状態の保存/復元は、IDが割り当てられたに対して自動的に処理されます。ドキュメントから:「デフォルトの実装は、IDを持つ階層内の各ビューを呼び出し、現在フォーカスされているビューのIDを保存することで、インスタンスごとのほとんどのUI状態を処理します(すべて復元されます)。)のデフォルト実装による」ViewonSaveInstanceStateonSaveInstanceState()onRestoreInstanceState(Bundle)
Vicky Chijwani

433

savedInstanceState唯一のAndroid破棄し、活動を再現した場合、それは前にあったように、それが戻って来ることができるように、例えば、現在のナビゲーションや選択の情報のため、活動の現在のインスタンスに関連付けられている状態を保存するためです。onCreateおよびのドキュメントを参照してくださいonSaveInstanceState

より長持ちする状態については、SQLiteデータベース、ファイル、または設定の使用を検討してください。永続的な状態の保存を参照してください。


3
いつsavedInstanceState == nullで、いつnullでないのですか?
Trojan.ZBOT 2013

6
システムがアクティビティの新しいインスタンスを作成している場合、savedInstanceStateはnullであり、復元している場合はnullではありません。
ガブリエルカマラ2014年

7
...これは、システムがアクティビティの新しいインスタンスをいつ作成する必要があるかという問題を提起します。アプリを終了するいくつかの方法ではバンドルが作成されないため、新しいインスタンスを作成する必要があります。これは根本的な問題です。つまり、バンドルの存在に依存することはできず、永続ストレージの代替手段を実行する必要があります。onSave / onRestoreInstanceStateの利点は、システムリソースをあまり消費することなく、システムが突然実行できるメカニズムであることです。そのため、それをサポートするだけでなく、アプリをより適切に終了するための永続的なストレージを用意することをお勧めします。
ToolmakerSteve '19

415

http://developer.android.com/reference/android/app/Activity.htmlのアクティビティの状態に関するドキュメントによると、永続的なデータの使用は安全ではないことに注意してください。onSaveInstanceStateonRestoreInstanceState

このドキュメントには次のように記載されています(「アクティビティライフサイクル」セクション):

後者はライフサイクルコールバックの一部ではなく、ドキュメントに記載されているようにすべての状況で呼び出されるわけではないため、onPause()ではなく永続データを保存することが重要onSaveInstanceState(Bundle)です。

つまり、永続データの保存/復元コードをonPause()onResume()

編集:さらに明確にするために、ここにonSaveInstanceState()ドキュメントがあります:

このメソッドは、アクティビティが強制終了される前に呼び出されるため、アクティビティがいつか戻ったときに、その状態を復元できます。たとえば、アクティビティBがアクティビティAの前で起動され、ある時点でアクティビティAが強制終了されてリソースを再利用する場合、アクティビティAはこのメソッドを介してユーザーインターフェイスの現在の状態を保存する機会があるため、ユーザーがアクティビティAでは、onCreate(Bundle)または を介してユーザーインターフェイスの状態を復元できますonRestoreInstanceState(Bundle)


55
ちょっとひっくり返すために、それも危険ではありません。これは、保持したい内容と期間に依存します。@ Bernardは、元の質問では完全には明確にされていません。InstanceStateは、現在のUI状態(コントロールに入力されたデータ、リスト内の現在の位置など)を保持するのに最適ですが、Pause / Resumeは、長期的な永続ストレージの唯一の可能性です。
Pontus Gagge

30
これは反対票です。ライフサイクルメソッドのようにon(Save | Restore)InstanceStateを使用するのは安全ではありません(つまり、状態を保存/復元する以外に何かを実行します)。状態の保存/復元に最適です。また、onPauseとonResumeでどのように状態を保存/復元しますか?使用できるこれらのメソッドではバンドルを取得しないため、データベース、ファイルなどで愚かな他の状態保存を使用する必要があります。
Felix

141
私たちはこの人に少なくとも反対票を投じるべきではありません。少なくとも彼はドキュメントを確認するために努力しました。私たちは私たちが知識のあるコミュニティを構築し、投票しないようにお互いを助け合うためにここにいると思います。ですから、その努力に1票を投じてください。反対票を投じないで、票を投じるか、投票しないようにお願いします。この人は、ドキュメントを通過する際の混乱を解消します。1票投票:)
AZ_10年

21
この回答は反対票に値するとは思わない。少なくとも彼は答えようと努力し、ドコからのセクションを引用した。
GSree 2011年

34
この回答は完全に正解であり、反対票ではなく賛成票に値します!それを見ない人のための州間の違いを明確にしましょう。選択されたラジオボタンや入力フィールドのテキストなどのGUI状態は、ListViewに表示されるリストに追加されたレコードなどのデータ状態ほど重要ではありません。後者は唯一の保証された呼び出しであるため、onPauseでデータベースに保存する必要があります。代わりにonSaveInstanceStateに入れた場合、それが呼び出されないとデータが失われる危険があります。しかし、同じ理由でラジオボタンの選択が保存されない場合、それは大した問題ではありません。
JBM 2011年

206

同僚が、アクティビティのライフサイクルと状態情報の説明、状態情報の保存方法、状態への保存など、Androidデバイスでのアプリケーションの状態を説明する記事を書きました Bundleし、SharedPreferencesそしてここを見てみましょう

この記事では、3つのアプローチについて説明します。

インスタンス状態バンドルを使用して、アプリケーションの有効期間中(つまり一時的に)のローカル変数/ UI制御データを保存します

[Code sample  Store state in state bundle]
@Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
  // Store UI state to the savedInstanceState.
  // This bundle will be passed to onCreate on next call.  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  savedInstanceState.putString(“Name”, strName);
  savedInstanceState.putString(“Email”, strEmail);
  savedInstanceState.putBoolean(“TandC”, blnTandC);

  super.onSaveInstanceState(savedInstanceState);
}

共有設定を使用して、アプリケーションインスタンス間で(つまり永続的に)ローカル変数/ UI制御データを保存します

[Code sample  store state in SharedPreferences]
@Override
protected void onPause()
{
  super.onPause();

  // Store values between instances here
  SharedPreferences preferences = getPreferences(MODE_PRIVATE);
  SharedPreferences.Editor editor = preferences.edit();  // Put the values from the UI
  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  editor.putString(“Name”, strName); // value to store
  editor.putString(“Email”, strEmail); // value to store
  editor.putBoolean(“TandC”, blnTandC); // value to store
  // Commit to storage
  editor.commit();
}

保持されている非構成インスタンスを使用して、アプリケーションの存続期間内のアクティビティ間でオブジェクトインスタンスをメモリ内に保持する

[Code sample  store object instance]
private cMyClassType moInstanceOfAClass; // Store the instance of an object
@Override
public Object onRetainNonConfigurationInstance()
{
  if (moInstanceOfAClass != null) // Check that the object exists
      return(moInstanceOfAClass);
  return super.onRetainNonConfigurationInstance();
}

3
@ MartinBelcher-Eigoの記事では、SharedPreferencesのデータについて、「このデータはデバイスのデータベースに書き込まれます。
トム

2
@Tom SharefPrefsデータはxmlファイルに書き込まれます。XMLは一種のデータベースですか?私はそうだと思います;)
MaciejGórski

148

これは、Android開発の典型的な「落とし穴」です。ここには2つの問題があります。

  • 少なくともレガシーバージョンでは、開発中のアプリケーションスタック管理を非常に複雑にする微妙なAndroidフレームワークのバグがあります(修正されたかどうか、いつ、どのように修正されたかは完全にはわかりません)。このバグについては以下で説明します。
  • この問題を管理する「通常の」または意図された方法自体は、onPause / onResumeとonSaveInstanceState / onRestoreInstanceStateの二重性によりかなり複雑です

これらすべてのスレッドを参照すると、多くの場合、開発者はこれら2つの異なる問題について同時に話し合っていると思われます。したがって、「これは私にとっては機能しません」という混乱と報告がすべてあります。

最初に、「意図された」動作を明確にするために:onSaveInstanceとonRestoreInstanceは脆弱であり、一時的な状態のみです。意図された使用法(afaict)は、電話が回転したときのアクティビティのレクリエーション(向きの変更)を処理することです。つまり、使用目的は、アクティビティがまだ論理的に「最上位」にあるが、システムによって再インスタンス化される必要がある場合です。保存されたバンドルは、プロセス/メモリ/ gcの外では永続化されないため、アクティビティがバックグラウンドで実行される場合、これに実際に依存することはできません。はい、おそらくあなたのアクティビティのメモリはバックグラウンドへの旅行を生き延びてGCをエスケープするでしょうが、これは信頼できません(予測できません)。

したがって、アプリケーションの「起動」間で持続する必要がある意味のある「ユーザーの進行状況」または状態があるシナリオがある場合、ガイダンスはonPauseとonResumeを使用することです。永続ストアを自分で選択して準備する必要があります。

しかし-これのすべてを複雑にする非常に紛らわしいバグがあります。詳細はこちら:

http://code.google.com/p/android/issues/detail?id=2373

http://code.google.com/p/android/issues/detail?id=5277

基本的に、アプリケーションがSingleTaskフラグで起動され、後でホーム画面またはランチャーメニューから起動した場合、その後の呼び出しで新しいタスクが作成されます...アプリの2つの異なるインスタンスが実質的に存在します。同じスタックに生息している...これは非常に奇妙に速くなります。これは、開発中に(EclipseまたはIntellijから)アプリを起動したときに発生するようで、開発者はこれに頻繁に遭遇します。しかし、アプリストアの更新メカニズムの一部を介して(ユーザーにも影響を与えるため)。

私の主な問題がこのバグであり、意図されたフレームワークの動作ではないことに気付く前に、私はこれらのスレッドを何時間も戦いました。素晴らしい記事と回避策 (更新:以下を参照)この回答のユーザー@kaciulaからのようです:

ホームキーを押す動作

2013年6月の更新:数か月後、ようやく「正しい」解決策が見つかりました。ステートフルのstartedAppフラグを自分で管理する必要はありません。これをフレームワークから検出し、適切に保釈することができます。LauncherActivity.onCreateの先頭近くでこれを使用します。

if (!isTaskRoot()) {
    Intent intent = getIntent();
    String action = intent.getAction();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
        finish();
        return;
    }
}

87

onSaveInstanceStateシステムがメモリを必要とし、アプリケーションを強制終了したときに呼び出されます。ユーザーがアプリケーションを閉じただけでは呼び出されません。だから私は、アプリケーションの状態も内に保存されるべきだと思うonPauseようないくつかの永続ストレージに保存されなければならないことPreferencesSqlite


36
申し訳ありませんが、それは正しくありません。onSaveInstanceStateは、アクティビティを再作成する必要がある前に呼び出されます。つまり、ユーザーがデバイスを回転させるたびに。一時的なビューステートを保存するためのものです。Androidがアプリケーションを強制的に閉じると、onSaveInstanceStateは実際には呼び出されません(そのため、重要なアプリケーションデータを保存するのは安全ではありません)。ただし、onPauseは、アクティビティが終了する前に呼び出されることが保証されているため、プリファレンスまたはSquliteに永続的な情報を格納するために使用する必要があります。正しい答え、間違った理由。
moveaway00 2012年

74

どちらの方法も便利で有効であり、さまざまなシナリオに最適です。

  1. ユーザーはアプリケーションを終了し、後日再オープンしますが、アプリケーションは最後のセッションからデータを再ロードする必要があります。これには、SQLiteを使用するなどの永続的なストレージアプローチが必要です。
  2. ユーザーがアプリケーションを切り替えてから元の状態に戻り、中断したところから再開したい場合-バンドルデータ(アプリケーションの状態データなど)を保存して復元しonSaveInstanceState()onRestoreInstanceState()通常はそれで十分です。

状態データを永続的に保存すると、onResume()or onCreate()(または実際には任意のライフサイクル呼び出し)で再ロードできます。これは望ましい動作である場合とそうでない場合があります。これをのバンドルにInstanceState保存する場合、一時的なものであり、同じユーザーの「セッション」で使用するためにデータを保存する場合にのみ適しています(私は「セッション」という用語を大まかに使用します)。

すべてのように、1つのアプローチが他のアプローチよりも優れているわけではありません。必要な動作を理解し、最も適切なアプローチを選択することが重要です。


70

状態を保存することは、私に関する限りせいぜい粗末です。永続的なデータを保存する必要がある場合は、SQLiteデータベースを使用してください。Androidを使えば、とても簡単です。

このようなもの:

import java.util.Date;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class dataHelper {

    private static final String DATABASE_NAME = "autoMate.db";
    private static final int DATABASE_VERSION = 1;

    private Context context;
    private SQLiteDatabase db;
    private OpenHelper oh ;

    public dataHelper(Context context) {
        this.context = context;
        this.oh = new OpenHelper(this.context);
        this.db = oh.getWritableDatabase();
    }

    public void close() {
        db.close();
        oh.close();
        db = null;
        oh = null;
        SQLiteDatabase.releaseMemory();
    }


    public void setCode(String codeName, Object codeValue, String codeDataType) {
        Cursor codeRow = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        String cv = "" ;

        if (codeDataType.toLowerCase().trim().equals("long") == true){
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            cv = String.valueOf(((Date)codeValue).getTime());
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            String.valueOf(codeValue);
        }
        else
        {
            cv = String.valueOf(codeValue);
        }

        if(codeRow.getCount() > 0) //exists-- update
        {
            db.execSQL("update code set codeValue = '" + cv +
                "' where codeName = '" + codeName + "'");
        }
        else // does not exist, insert
        {
            db.execSQL("INSERT INTO code (codeName, codeValue, codeDataType) VALUES(" +
                    "'" + codeName + "'," +
                    "'" + cv + "'," +
                    "'" + codeDataType + "')" );
        }
    }

    public Object getCode(String codeName, Object defaultValue){

        //Check to see if it already exists
        String codeValue = "";
        String codeDataType = "";
        boolean found = false;
        Cursor codeRow  = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        if (codeRow.moveToFirst())
        {
            codeValue = codeRow.getString(codeRow.getColumnIndex("codeValue"));
            codeDataType = codeRow.getString(codeRow.getColumnIndex("codeDataType"));
            found = true;
        }

        if (found == false)
        {
            return defaultValue;
        }
        else if (codeDataType.toLowerCase().trim().equals("long") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (long)0;
            }
            return Long.parseLong(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (int)0;
            }
            return Integer.parseInt(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            if (codeValue.equals("") == true)
            {
                return null;
            }
            return new Date(Long.parseLong(codeValue));
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            if (codeValue.equals("") == true)
            {
                return false;
            }
            return Boolean.parseBoolean(codeValue);
        }
        else
        {
            return (String)codeValue;
        }
    }


    private static class OpenHelper extends SQLiteOpenHelper {

        OpenHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE IF  NOT EXISTS code" +
            "(id INTEGER PRIMARY KEY, codeName TEXT, codeValue TEXT, codeDataType TEXT)");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }
}

その後の簡単な呼び出し

dataHelper dh = new dataHelper(getBaseContext());
String status = (String) dh.getCode("appState", "safetyDisabled");
Date serviceStart = (Date) dh.getCode("serviceStartTime", null);
dh.close();
dh = null;

9
SQLiteデータベースのロードには時間がかかりすぎるため、これがユーザーにアプリのUIを表示するためのクリティカルパス上にあることを考慮してください。実際に時間を計測していないので、修正してよかったのですが、データベースファイルの読み込みと開くのは速くありませんか?
トム

5
初心者が自分のアプリにカットアンドペーストしてすぐに使用できるソリューションを提供していただき、ありがとうございます。@Tom速度に関しては、1000ペアを格納するのに約7秒かかりますが、AsyncTaskで実行できます。ただし、finally {cursor.close()}を追加する必要があります。追加しないと、メモリリークが原因でクラッシュします。
Noumenon 2013年

3
私はこれに遭遇しました、そしてそれは私が最近取り組んでいる/または最近使っているデバイスであるGoogle Glassでこれを利用することを試みるのをきちんとしているようですがきちんと思われます。
Stephen Tetreault 2014年

61

私は答えを見つけたと思います。私が何をしたかを簡単な言葉で説明しましょう:

アクティビティ1とアクティビティ2の2つのアクティビティがあり、アクティビティ1からアクティビティ2に移動して(アクティビティ2でいくつかの作業を行った)、アクティビティ1のボタンをクリックしてアクティビティ1に戻るとします。この段階で、アクティビティ2に戻り、最後にアクティビティ2を終了したときと同じ状態でアクティビティ2を確認したいと思います。

上記のシナリオで私が行ったことは、マニフェストで次のような変更を加えたことです。

<activity android:name=".activity2"
          android:alwaysRetainTaskState="true"      
          android:launchMode="singleInstance">
</activity>

そして、ボタンクリックイベントのactivity1で、次のようにしました。

Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setClassName(this,"com.mainscreen.activity2");
startActivity(intent);

そしてボタンクリックイベントのactivity2で私はこのようにしました:

Intent intent=new Intent();
intent.setClassName(this,"com.mainscreen.activity1");
startActivity(intent);

これで、Activity2で行った変更は失われず、Activity2を以前と同じ状態で表示できるようになります。

私はこれが答えだと信じており、これは私にとってはうまくいきます。私が間違っていたら訂正してください。


2
@bagusflyerより具体的に注意してください??? あなたのコメントは役に立たず、それに基づいて誰もあなたを助けることはできません。
Stephen Tetreault 2014年

2
これは、異なる状況に対する答えです。同じアプリ内の2つのアクティビティです。OPはアプリを終了することです(たとえば、ホームボタン、または別のアプリに切り替える他の手段)。
ToolmakerSteve 2015

44

onSaveInstanceState()一時データ(onCreate()/に復元onRestoreInstanceState())、onPause()永続データ(/に復元onResume())Android技術リソースから:

onSaveInstanceState()は、アクティビティが停止されている場合にAndroidによって呼び出され、再開される前に強制終了される可能性があります。つまり、アクティビティが再起動されたときに、同じ状態に再初期化するために必要な状態を保存する必要があります。これは、onCreate()メソッドに対応するものであり、実際に、onCreate()に渡されるsavedInstanceState Bundleは、onSaveInstanceState()メソッドでoutStateとして作成するバンドルと同じです。

onPause()onResume()も補完的なメソッドです。onPause()は、(finish()呼び出しなどで)開始した場合でも、アクティビティが終了すると常に呼び出されます。これを使用して、現在のメモをデータベースに保存します。onPause()中に解放できるリソースを解放し、パッシブ状態のときに使用するリソースを少なくすることをお勧めします。


40

onSaveInstanceState()アクティビティがバックグラウンドになると、実際に呼び出されます。

ドキュメントからの引用:「このメソッドは、アクティビティが強制終了される前に呼び出されるため、しばらくして戻ってきたときに状態を復元できます。」 ソース


37

助けるために、私は次のように使う決まり文句を削減interfaceし、class読むために/書き込みをするBundleインスタンス状態を保存します。


まず、インスタンス変数に注釈を付けるために使用されるインターフェースを作成します。

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
        ElementType.FIELD
})
public @interface SaveInstance {

}

次に、リフレクションを使用して値をバンドルに保存するクラスを作成します。

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;

import java.io.Serializable;
import java.lang.reflect.Field;

/**
 * Save and load fields to/from a {@link Bundle}. All fields should be annotated with {@link
 * SaveInstance}.</p>
 */
public class Icicle {

    private static final String TAG = "Icicle";

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #load(Bundle, Object)
     */
    public static void save(Bundle outState, Object classInstance) {
        save(outState, classInstance, classInstance.getClass());
    }

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #load(Bundle, Object, Class)
     */
    public static void save(Bundle outState, Object classInstance, Class<?> baseClass) {
        if (outState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    field.setAccessible(true);
                    String key = className + "#" + field.getName();
                    try {
                        Object value = field.get(classInstance);
                        if (value instanceof Parcelable) {
                            outState.putParcelable(key, (Parcelable) value);
                        } else if (value instanceof Serializable) {
                            outState.putSerializable(key, (Serializable) value);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not added to the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #save(Bundle, Object)
     */
    public static void load(Bundle savedInstanceState, Object classInstance) {
        load(savedInstanceState, classInstance, classInstance.getClass());
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #save(Bundle, Object, Class)
     */
    public static void load(Bundle savedInstanceState, Object classInstance, Class<?> baseClass) {
        if (savedInstanceState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    String key = className + "#" + field.getName();
                    field.setAccessible(true);
                    try {
                        Object fieldVal = savedInstanceState.get(key);
                        if (fieldVal != null) {
                            field.set(classInstance, fieldVal);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not retrieved from the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

}

使用例:

public class MainActivity extends Activity {

    @SaveInstance
    private String foo;

    @SaveInstance
    private int bar;

    @SaveInstance
    private Intent baz;

    @SaveInstance
    private boolean qux;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Icicle.load(savedInstanceState, this);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Icicle.save(outState, this);
    }

}

注:このコードは、MITライセンスの下でライセンスされているAndroidAutowireというライブラリプロジェクトから改作されました。


34

その間、私は一般的にこれ以上使用しません

Bundle savedInstanceState & Co

ライフサイクルはほとんどのアクティビティにとって複雑すぎて、必要ありません。

そしてグーグルはそれ自体を述べています、それは信頼できません。

私の方法は、設定に変更をすぐに保存することです:

 SharedPreferences p;
 p.edit().put(..).commit()

ある意味で、SharedPreferencesはバンドルと同様に機能します。そして当然のことながら、最初はそのような値を設定から読み取る必要があります。

複雑なデータの場合、設定を使用する代わりにSQLiteを使用できます。

この概念を適用すると、アクティビティは、リブートを伴う最初のオープンであったか、バックスタックによる再オープンであったかに関係なく、最後に保存された状態を使用し続けます。


31

元の質問に直接回答する。アクティビティが再作成されることはないため、savedInstancestateはnullです。

アクティビティは、次の場合にのみステートバンドルで再作成されます。

  • 新しいアクティビティインスタンスの作成が必要になる可能性がある、向きや電話の言語の変更などの構成変更。
  • OSがアクティビティを破棄した後、バックグラウンドからアプリに戻ります。

Androidは、メモリの負荷がかかっているとき、またはバックグラウンドで長時間にわたっていると、バックグラウンドアクティビティを破棄します。

Hello Worldの例をテストする場合、アクティビティを離れてアクティビティに戻るにはいくつかの方法があります。

  • 戻るボタンを押すと、アクティビティが終了します。アプリの再起動はまったく新しいインスタンスです。バックグラウンドからまったく再開していません。
  • ホームボタンを押すか、タスクスイッチャーを使用すると、アクティビティがバックグラウンドになります。アプリケーションに戻るときに、アクティビティを破棄する必要がある場合にのみ、onCreateが呼び出されます。

ほとんどの場合、家を押してアプリを再度起動するだけであれば、アクティビティを再作成する必要はありません。これはすでにメモリに存在するため、onCreate()は呼び出されません。

[設定]-> [開発者向けオプション]の下に、[アクティビティを保持しない]というオプションがあります。有効にすると、Androidは常にアクティビティを破棄し、バックグラウンドのときにアクティビティを再作成します。これは最悪のシナリオをシミュレートするため、開発時に有効のままにしておくのに最適なオプションです。(常にあなたの活動をリサイクルする低メモリデバイス)。

他の答えは、状態を保存する正しい方法を教えてくれるという点で価値がありますが、コードが期待どおりに機能しなかった理由が実際に答えられたとは思いませんでした。


28

onSaveInstanceState(bundle)およびonRestoreInstanceState(bundle)方法は、スクリーン(姿勢変化)を回転させながら、単にデータの永続性のために有用です。
彼らは、アプリケーション間で切り替えながらので、(たとえ良いものではありませんonSaveInstanceState()メソッドが呼び出されたが、onCreate(bundle)onRestoreInstanceState(bundle)再度呼び出されることはありません。
多くの永続使用するために好みを共有した。この記事を読んで


2
あなたの場合onCreate、あなたonRestoreInstanceStateActivityアプリを切り替えたときにまったく破壊されないので呼び出されていませんので、何も復元する必要はありません。AndroidはonSaveInstanceState、アクティビティが後で破棄される場合に備えて呼び出します(デバイス全体の構成が変更され、アクティビティを最初から再作成する必要があるため、画面を回転すると100%確実に発生します)。
Vicky Chijwani

20

私の問題は、アプリケーションの存続期間(つまり、同じアプリ内で他のサブアクティビティを開始したり、デバイスを回転したりすることを含む1回の実行)の間だけ永続性が必要だったことです。上記の回答のさまざまな組み合わせを試しましたが、すべての状況で希望どおりの結果が得られませんでした。結局、私のために働いたのは、onCreate中にsavedInstanceStateへの参照を取得することでした。

mySavedInstanceState=savedInstanceState;

そして、それを使用して、必要に応じて、次の行に沿って変数の内容を取得します。

if (mySavedInstanceState !=null) {
   boolean myVariable = mySavedInstanceState.getBoolean("MyVariable");
}

私はとを上で提案したように使用onSaveInstanceStateonRestoreInstanceStateていますが、変数が変更されたときに変数を保存するためにメソッドを使用することもできます(例:を使用putBoolean


19

受け入れられた答えは正しいですが、Icepickと呼ばれるライブラリを使用して、Androidでアクティビティの状態を保存するより速くて簡単な方法があります。Icepickは、状態の保存と復元に使用されるすべての定型コードを処理する注釈プロセッサです。

Icepickでこのようなことをする:

class MainActivity extends Activity {
  @State String username; // These will be automatically saved and restored
  @State String password;
  @State int age;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState);
  }

  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState);
  }
}

これを行うのと同じです:

class MainActivity extends Activity {
  String username;
  String password;
  int age;

  @Override
  public void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putString("MyString", username);
    savedInstanceState.putString("MyPassword", password);
    savedInstanceState.putInt("MyAge", age); 
    /* remember you would need to actually initialize these variables before putting it in the
    Bundle */
  }

  @Override
  public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    username = savedInstanceState.getString("MyString");
    password = savedInstanceState.getString("MyPassword");
    age = savedInstanceState.getInt("MyAge");
  }
}

Icepickは、状態をで保存するすべてのオブジェクトで機能しますBundle


16

アクティビティが作成されると、そのonCreate()メソッドが呼び出されます。

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

savedInstanceStateは、初めてnullであるBundleクラスのオブジェクトですが、再作成されたときに値が含まれています。アクティビティの状態を保存するには、onSaveInstanceState()をオーバーライドする必要があります。

   @Override
    protected void onSaveInstanceState(Bundle outState) {
      outState.putString("key","Welcome Back")
        super.onSaveInstanceState(outState);       //save state
    }

outState.putString( "key"、 "Welcome Back")のような "outState" Bundleオブジェクトに値を入れ、superを呼び出して保存します。アクティビティが破棄されると、その状態はBundleオブジェクトに保存され、onCreate()またはonRestoreInstanceState()で再作成した後に復元できます。onCreate()とonRestoreInstanceState()で受け取ったバンドルは同じです。

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

          //restore activity's state
         if(savedInstanceState!=null){
          String reStoredString=savedInstanceState.getString("key");
            }
    }

または

  //restores activity's saved state
 @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
      String restoredMessage=savedInstanceState.getString("key");
    }

15

この変更を実装するには、基本的に2つの方法があります。

  1. とを使用onSaveInstanceState()onRestoreInstanceState()ます。
  2. マニフェストでandroid:configChanges="orientation|screenSize"

私は2番目の方法を使用することをお勧めしません。私の経験の1つでは、縦向きから横向きに、またはその逆に回転しているときに、デバイス画面の半分が黒くなっていたためです。

上記の最初の方法を使用すると、方向が変更されたとき、または構成が変更されたときにデータを保持できます。savedInstance状態オブジェクト内に任意のタイプのデータを格納する方法を知っています。

例:Jsonオブジェクトを永続化する場合を考えます。ゲッターとセッターを持つモデルクラスを作成します。

class MyModel extends Serializable{
JSONObject obj;

setJsonObject(JsonObject obj)
{
this.obj=obj;
}

JSONObject getJsonObject()
return this.obj;
} 
}

次に、onCreateおよびonSaveInstanceStateメソッドのアクティビティで、次の手順を実行します。次のようになります。

@override
onCreate(Bundle savedInstaceState){
MyModel data= (MyModel)savedInstaceState.getSerializable("yourkey")
JSONObject obj=data.getJsonObject();
//Here you have retained JSONObject and can use.
}


@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Obj is some json object 
MyModel dataToSave= new MyModel();
dataToSave.setJsonObject(obj);
oustate.putSerializable("yourkey",dataToSave); 

}

11

ここからのコメントであるスティーブ・モーズリーの答え(によるToolmakerSteveは)そのような観点に入れもの(onPause対全体onSaveInstanceStateで、西コスト佐賀VS東コスト)

@VVK-部分的に同意しません。アプリを終了するいくつかの方法では、onSaveInstanceState(oSIS)がトリガーされません。これはoSISの有用性を制限します。最小限のOSリソースでサポートする価値がありますが、アプリがユーザーを元の状態に戻したい場合は、アプリがどのように終了したかに関係なく、代わりに永続ストレージアプローチを使用する必要があります。 onCreateを使用してバンドルを確認し、見つからない場合は 永続ストレージを確認します。これにより、意思決定が一元化されます。クラッシュから回復したり、戻るボタンで終了したり、カスタムメニュー項目の終了で終了したり、ユーザーが何日か後に画面に戻ったりした。– ToolmakerSteve 2015年9月19日10:38


10

Kotlinコード:

セーブ:

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState.apply {
        putInt("intKey", 1)
        putString("stringKey", "String Value")
        putParcelable("parcelableKey", parcelableObject)
    })
}

その後、中onCreate()またはonRestoreInstanceState()

    val restoredInt = savedInstanceState?.getInt("intKey") ?: 1 //default int
    val restoredString = savedInstanceState?.getString("stringKey") ?: "default string"
    val restoredParcelable = savedInstanceState?.getParcelable<ParcelableClass>("parcelableKey") ?: ParcelableClass() //default parcelable

オプションを使用したくない場合は、デフォルト値を追加します


9

に保存されているアクティビティ状態データを取得するにはonCreate()、まずSaveInstanceState(Bundle savedInstanceState)メソッドをオーバーライドして、savedInstanceStateにデータを保存する必要があります。

アクティビティdestroy SaveInstanceState(Bundle savedInstanceState)メソッドが呼び出され、そこに保存したいデータを保存すると、そしてonCreate()、アクティビティが再開したときも同じです(アクティビティが破棄される前にデータを保存しているため、savedInstanceStateはnullになりません)。


6

この問題を解決するには、IcePickを使用するのが簡単です。

まず、ライブラリをセットアップします app/build.gradle

repositories {
  maven {url "https://clojars.org/repo/"}
}
dependencies {
  compile 'frankiesardo:icepick:3.2.0'
  provided 'frankiesardo:icepick-processor:3.2.0'
}

さて、アクティビティの状態を保存する方法を以下の例で確認してみましょう

public class ExampleActivity extends Activity {
  @State String username; // This will be automatically saved and restored

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState);
  }

  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState);
  }
}

これは、アクティビティ、フラグメント、またはバンドルでの状態をシリアル化する必要がある任意のオブジェクト(たとえば、モルタルのViewPresenters)で機能します

Icepickは、カスタムビューのインスタンス状態コードも生成できます。

class CustomView extends View {
  @State int selectedPosition; // This will be automatically saved and restored

  @Override public Parcelable onSaveInstanceState() {
    return Icepick.saveInstanceState(this, super.onSaveInstanceState());
  }

  @Override public void onRestoreInstanceState(Parcelable state) {
    super.onRestoreInstanceState(Icepick.restoreInstanceState(this, state));
  }

  // You can put the calls to Icepick into a BaseCustomView and inherit from it
  // All Views extending this CustomView automatically have state saved/restored
}

1
@ralphspoonはい、フラグメントとカスタムビューで機能します。サンプルコードを確認してください。回答を編集しました。コードのサンプルを探すには、github.com / frankiesardo / icepickの公式ドキュメントにアクセスすることをお勧めします。
THANN Phearum 2016

@ChetanMehraあなたはカスタムビュークラスを意味しますよね?カスタムビューの場合は、上記のCustomViewの例のように、onSaveInstanceStateとonRestoreInstanceStateをオーバーライドできます。
THANN Phearum

たとえば、ビュークラス内のクラスオブジェクトを意味します:クラスCustomView extends View {@State ClassA a;}またはクラスCustomView extends View {@ State Inner class {}}
Chetan Mehra

@THANNPhearum私は別の質問としてそれを尋ねるべきですか?
Chetan Mehra

そうですか。その場合、ClassAはParcelableである必要があります。それがアクティビティ、フラグメント、またはバンドルでその状態をシリアル化する必要がある任意のオブジェクトで機能すると述べたように
THANN Phearum

6

私のソリューションが不快に感じられるかどうかはわかりませんが、バインドされたサービスを使用してViewModelの状態を永続化します。サービスのメモリに保存するか、永続化してSQLiteデータベースから取得するかは、要件によって異なります。これは、あらゆるフレーバーのサービスが行うことであり、アプリケーションの状態の維持や一般的なビジネスロジックの抽象化などのサービスを提供します。

モバイルデバイスに固有のメモリと処理の制約のため、AndroidビューはWebページと同じように扱います。ページは状態を維持せず、純粋にプレゼンテーション層コンポーネントであり、その唯一の目的は、アプリケーションの状態を提示し、ユーザー入力を受け入れることです。Webアプリアーキテクチャの最近の傾向では、ページがビューであり、ドメインデータがモデルであり、コントローラーがWebサービスの背後にある、古くなったモデル、ビュー、コントローラー(MVC)パターンの使用を採用しています。同じパターンをビューでもAndroidで使用できます。ビューはビューです。モデルはドメインデータであり、コントローラーはAndroidバインドサービスとして実装されます。ビューがコントローラーと対話するようにしたいときはいつでも、開始/再開時にビューにバインドし、停止/一時停止時にバインドを解除します。

このアプローチにより、アプリケーションビジネスロジックのすべてをサービスに移動できるため、関心の分離の設計原則を適用できるという追加のボーナスが得られます。これにより、複数のビュー間で重複するロジックが削減され、ビューが別の重要な設計原則である単一の責任を強制できるようになります。


5

コトリン

あなたはオーバーライドする必要がありますonSaveInstanceStateし、onRestoreInstanceState永続化したい変数をて保存および取得

ライフサイクルグラフ

変数を保存する

public override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)

    // prepare variables here
    savedInstanceState.putInt("kInt", 10)
    savedInstanceState.putBoolean("kBool", true)
    savedInstanceState.putDouble("kDouble", 4.5)
    savedInstanceState.putString("kString", "Hello Kotlin")
}

変数を取得する

public override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)

    val myInt = savedInstanceState.getInt("kInt")
    val myBoolean = savedInstanceState.getBoolean("kBool")
    val myDouble = savedInstanceState.getDouble("kDouble")
    val myString = savedInstanceState.getString("kString")
    // use variables here
}

2

Androidが状態を保存するためのViewModelを提供するようになったので、saveInstanceStateの代わりにそれを使用してみてください。


3
本当じゃない。ドキュメントから:「保存されたインスタンスの状態とは異なり、ViewModelはシステム起動プロセスの終了時に破棄されます。これが、ViewModelオブジェクトをonSaveInstanceState()(または他のディスク永続化)と組み合わせて使用​​し、savedInstanceStateで識別子を隠してビューを支援する必要がある理由です。モデルはシステムの停止後にデータを再ロードします。」
Vyacheslav Martynenko 2018年

バックグラウンドで変更された権限でこれに遭遇しました。
Brill Pappin、2018

私は同意します。「システムが開始したプロセスの終了を処理する必要がある場合は、onSaveInstanceState()をバックアップとして使用することをお勧めします。」
Zhar

2

Androidにメソッドを実装せずに状態を保存させる方法があります。この行をアクティビティ宣言のマニフェストに追加するだけです。

android:configChanges="orientation|screenSize"

次のようになります。

<activity
    android:name=".activities.MyActivity"
    android:configChanges="orientation|screenSize">
</activity>

ここでは、このプロパティに関する詳細情報を見つけることができます。

手動で処理するよりも、Androidでこれを処理することをお勧めします。


2
これは状態の保存とは関係ありません。方向の変更をあきらめるだけで、アプリはさまざまなイベントでいつでも再起動、一時停止、再開できることに注意してください
lord-ralf-adolf

1
この答えは、向きが変わったときに状態を保存し、複雑な方法の理解と実装を避けたい人のためです
IgniteCoders

かなり公平にあなたのポイントを見ると、私は状態を保存するのに苦労しているほとんどの人がフラグメントを使用していると思います。なぜなら、アクティビティが実際にIDを持っている限り、UIコンポーネントの統計を保存するからです。彼らは再び、インスタンスの保存ステータスを処理するのは
面倒でした

動作します...ありがとう
ファナデス

1

何を保存し、何を保存しないか?

EditText方向が変更されている間、なぜのテキストが自動的に保存されるのか疑問に思ったことはありませんか?さて、この答えはあなたのためです。

アクティビティのインスタンスが破棄され、システムが新しいインスタンスを再作成したとき(構成の変更など)。古いアクティビティ状態(インスタンス状態)の保存されたデータのセットを使用して、それを再作成しようとします)。

インスタンス状態は、オブジェクトに格納されているキーと値のペアのコレクションですBundle

デフォルトでは、たとえば、システムはビューオブジェクトをバンドルに保存します。

  • テキストイン EditText
  • のスクロール位置ListViewなど

インスタンス状態の一部として別の変数を保存する必要がある場合は、メソッドをオーバーライド する必要がありますonSavedInstanceState(Bundle savedinstaneState)

たとえばint currentScore、GameActivity

データの保存中のonSavedInstanceState(Bundle savedinstaneState)の詳細

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}

したがってsuper.onSaveInstanceState(savedInstanceState);、デフォルトの動作を呼び出すのを忘れた場合、誤っ て機能しません。つまり、EditTextのテキストは保存されません。

アクティビティの状態を復元するためにどちらを選択しますか?

 onCreate(Bundle savedInstanceState)

または

onRestoreInstanceState(Bundle savedInstanceState)

どちらのメソッドも同じBundleオブジェクトを取得するため、復元ロジックをどこに記述するかは問題ではありません。唯一の違いは、onCreate(Bundle savedInstanceState)メソッドではnullチェックを指定する必要があることですが、後者の場合は必要ありません。他の回答にはすでにコードスニペットがあります。あなたはそれらを参照することができます。

onRestoreInstanceState(Bundle savedinstaneState)の詳細

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from the saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
}

super.onRestoreInstanceState(savedInstanceState);システムがデフォルトでビュー階層を復元するように常に呼び出します

ボーナス

onSaveInstanceState(Bundle savedInstanceState)ユーザーが活動に戻ってくるために意図した場合にのみ、システムによって呼び出されます。たとえば、App Xを使用していて、突然電話がかかってきたとします。発信者のアプリに移動し、アプリXに戻ります。この場合、onSaveInstanceState(Bundle savedInstanceState)ます。メソッドが呼び出されます。

ただし、ユーザーが[戻る]ボタンを押した場合は、これを考慮してください。ユーザーはアクティビティに戻るつもりがないと想定されているため、この場合onSaveInstanceState(Bundle savedInstanceState)、システムによって呼び出されません。ポイントは、データを保存するときにすべてのシナリオを考慮する必要があるということです。

関連リンク:

デフォルトの動作に関するデモ
Android Official Documentation


1

ここで、ビューモデルで2つの方法を実行することは理にかなっています。最初のものを保存済みインスタンスとして保存する場合:このようにビューモデルに状態パラメーターを追加できます https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate#javaのます。

または、変数またはオブジェクトをビューモデルに保存できます。この場合、ビューモデルは、アクティビティが破棄されるまでライフサイクルを保持します。

public class HelloAndroidViewModel extends ViewModel {
   public Booelan firstInit = false;

    public HelloAndroidViewModel() {
        firstInit = false;
    }
    ...
}

public class HelloAndroid extends Activity {

  private TextView mTextView = null;
  HelloAndroidViewModel viewModel = ViewModelProviders.of(this).get(HelloAndroidViewModel.class);
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    //Because even if the state is deleted, the data in the viewmodel will be kept because the activity does not destroy
    if(!viewModel.firstInit){
        viewModel.firstInit = true
        mTextView.setText("Welcome to HelloAndroid!");
    }else{
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}

あなたは正しいですが、このライブラリはまだリリース中であるので、待つ必要があると思います...
Zhar

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.