web-dev-qa-db-ja.com

Android表示-アクティビティで自動的に保存および復元されるもの

私はAndroidの初心者です。

Androidでは、一部の一般的な要素を自動的にonSaveInstanceState/onRestoreInstanceStateに保存/復元できます。

たとえば、EditTextTextプロパティを保存/復元し、RatingBarRatingプロパティを保存/復元します...

いくつかのテストでわかりましたが、ドキュメントではこれについて何も見つかりません。

私の介入なしに、暗黙的に保存/復元されたものをどのように知ることができますか?

たとえば、EditText.Textは自動的に保存/復元されますか?

特に、すべてのプロパティをテストする必要はありません。


JRG回答から編集:

https://developer.Android.com/guide/components/activities/activity-lifecycle.html

アクティビティを保存しますアクティビティが停止し始めると、システムはonSaveInstanceState()メソッドを呼び出します<...>このメソッドのデフォルトの実装は一時的なEditTextウィジェットのテキストやListViewウィジェットのスクロール位置など、アクティビティのビュー階層の状態に関する情報。

保存/復元のデフォルトの実装を知るにはどうすればよいですか?

JRG回答を再読み込みした後の2番目の編集:

デフォルトでは、システムはBundleインスタンスの状態を使用して、アクティビティレイアウトの各Viewオブジェクトに関する情報を保存します(EditTextウィジェットに入力されたテキスト値など)。

デフォルトの実装では、要素ビューのすべての状態が保存/復元されます。

10
Orwel

状態の保存について説明するAndroidドキュメントと、アクティビティとフラグメントでの状態の保存に関する非常に優れた記事。

アクティビティ状態の保存と復元ユーザーが[戻る]ボタンを押したときなど、アプリの通常の動作が原因でアクティビティが破棄されるシナリオがいくつかありますアクティビティは、finish()メソッドを呼び出すことにより、自身の破棄を通知します。アクティビティがStopped状態であり、長期間使用されていない場合、またはフォアグラウンドアクティビティがさらにリソースを必要とする場合、システムはアクティビティを含むプロセスを破壊してメモリを回復する場合もあります。

ユーザーが[戻る]を押すか、アクティビティが終了したためにアクティビティが破棄されると、そのアクティビティは不要になったため、システムはそのアクティビティインスタンスの概念を永久に失います。ただし、システムが(通常のアプリの動作ではなく)システムの制約によりアクティビティを破棄した場合、実際のアクティビティインスタンスは失われますが、システムはそれが存在していたことを記憶しているため、ユーザーがそこに戻った場合、システムは新しいアクティビティを作成します。破棄されたときのアクティビティの状態を記述する保存されたデータのセットを使用するアクティビティのインスタンス。システムが以前の状態を復元するために使用する保存されたデータは、インスタンス状態と呼ばれ、Bundleオブジェクトに格納されているキーと値のペアのコレクションです。

デフォルトでは、システムはBundleインスタンス状態を使用して、アクティビティレイアウト内の各Viewオブジェクトに関する情報(EditTextウィジェットに入力されたテキスト値など)を保存します。 したがって、アクティビティインスタンスが破棄されて再作成された場合、レイアウトの状態は、コードを必要とせずに以前の状態に復元されます。ただし、アクティビティには、アクティビティでのユーザーの進行状況を追跡するメンバー変数など、復元したい状態情報がさらに含まれている場合があります。

アクティビティの状態を保存しますアクティビティが停止し始めると、システムがonSaveInstanceState()メソッドを呼び出し、アクティビティがキーのコレクションで状態情報を保存できるようにします-値のペア。このメソッドのデフォルトの実装は、EditTextウィジェットのテキストやListViewウィジェットのスクロール位置など、アクティビティのビュー階層の状態に関する一時的な情報を保存します。アプリは、onPause()メソッドの後、onStop()の前にonSaveInstanceState()コールバックを実装する必要があります。このコールバックをonPause()に実装しないでください。

注意:デフォルトの実装がビュー階層の状態を保存できるように、常にonSaveInstanceState()のスーパークラス実装を呼び出す必要があります。

アクティビティの追加の状態情報を保存するには、onSaveInstanceState()をオーバーライドして、アクティビティが予期せず破棄された場合に保存されるBundleオブジェクトにキーと値のペアを追加する必要があります。例えば:

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...


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


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

注:Androidシステムがアクティビティ内のビューの状態を復元するには、各ビューにAndroid:id属性によって提供される一意のIDが必要です。

ユーザー設定やデータベースのデータなどの永続的なデータを保存するには、アクティビティがフォアグラウンドにあるときに適切な機会をとる必要があります。そのような機会が発生しない場合は、onStop()メソッドの実行中にそのようなデータを保存する必要があります。

アクティビティの状態を復元しますアクティビティが以前に破棄された後に再作成された場合、システムがアクティビティに渡すバンドルから保存した状態を復元できます。 onCreate()とonRestoreInstanceState()の両方のコールバックメソッドは、インスタンスの状態情報を含む同じバンドルを受け取ります。

OnCreate()メソッドは、システムがアクティビティの新しいインスタンスを作成している場合でも、以前のインスタンスを再作成している場合でも呼び出されるため、状態バンドルがnullかどうかを確認してから読み取ってください。 nullの場合、システムは、破棄された以前のインスタンスを復元するのではなく、アクティビティの新しいインスタンスを作成します。

たとえば、次のコードスニペットは、onCreate()で状態データを復元する方法を示しています。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first


    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
        // Restore value of members from saved state
        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance
    }
    ...
}

OnCreate()中に状態を復元する代わりに、システムがonStart()メソッドの後に呼び出すonRestoreInstanceState()の実装を選択できます。システムがonRestoreInstanceState()を呼び出すのは、復元する保存済みの状態がある場合のみなので、バンドルがnullであるかどうかを確認する必要はありません。

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


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

注意:デフォルトの実装でビュー階層の状態を復元できるように、常にonRestoreInstanceState()のスーパークラス実装を呼び出してください。

4
JRG

これはあなたを説明する例です...

  • デフォルトでは何がどのように保存されますか?
  • 保存するためにコードを追加する必要があるのはどのデータですか?

私はシンプルなAndroidプロジェクトを作成しました。このプロジェクトには、アプリのライフサイクルのある時点で何らかの値を持つ合計4つのデータポイントがあります。

  1. アクティビティ内部変数saveMe
  2. アクティビティ内部変数saveMeNot
  3. ビューのEditText withid(Android:idを持っています)
  4. ビューのEditText(Android:idはありません)

スクリーンショットのとおりの一連のイベントは次のとおりです。

  1. Androidアプリを起動します
  2. SAVEボタンをクリックして、内部変数saveMeおよびsaveMeNotの値を設定します。 Toastは、両方の変数の値を保存したことが表示されます。
  3. こんにちはとこんにちはのような編集テキストの両方にテキストを入力します。これにより、両方の編集テキストにテキストが設定されます。
  4. 画面を回転します。つまり、向きを変更します。以下が発生します....
    • Androidは、Android:idactivity_main.xmlが定義されているすべてのビューの値を保存します。ここでは、Helloのみが保存されます。Helloが入力されたEditTextにはAndroid:id=@+id/withIdが含まれます。テキストHiを含む別のEditTextは、Androidがないため自動的に保存されません。Android:idがないためです。これは、すべてのビューにAndroid:idが定義されています。ビューを拡張するカスタムビューがある場合は、Android:idも定義されています。
    • Androidは、アクティビティのすべての内部変数、つまりsaveMesaveMeNotの状態を保存する機能を提供するonSaveInstanceStateとonRestoreInstanceStateも呼び出します。あなたはそれをコード化する必要があるでしょう、そうでなければ状態は失われます。私の例のように、saveMeではなくsaveMeNotの状態を保存しました。これは無料では得られないものです。つまり、コードを記述する必要があります。
  5. saveMesaveMeNotの値を表示するには、CLICK MEボタンをクリックしてください。saveMeの値は、onSaveInstanceStateに保存され、onRestoreInstanceStateで取得されたため、表示されます。

ステップのシーケンス

enter image description here

コード

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
    package="test.saveinstance">

    <application
        Android:allowBackup="true"
        Android:icon="@mipmap/ic_launcher"
        Android:label="@string/app_name"
        Android:roundIcon="@mipmap/ic_launcher_round"
        Android:supportsRtl="true"
        Android:theme="@style/AppTheme">
        <activity Android:name=".MainActivity">
            <intent-filter>
                <action Android:name="Android.intent.action.MAIN" />

                <category Android:name="Android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

MainActivity.Java

package test.saveinstance;

import Android.support.v7.app.AppCompatActivity;
import Android.os.Bundle;
import Android.util.Log;
import Android.view.View;
import Android.widget.Button;
import Android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    // will save in bundle in onSaveInstanceState
    private int saveMe;

    // will not save in bundle in onSaveInstanceState
    private int saveMeNot;

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

        // do some action that generates values for
        // activity specific variables i.e. saveMe
        // and saveMeNot
        Button saveButton = (Button) findViewById(R.id.save);
        saveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                saveMe = 10;
                saveMeNot = 20;
                Toast.makeText(getApplicationContext(), "SAVED: saveMe: " + saveMe + ";saveMeNot: " + saveMeNot, Toast.LENGTH_LONG).show();
            }
        });

        // will be used to display value of
        // saveMe and saveMeNot after orientation
        // changes.
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getApplicationContext(), "DISPLAY: saveMe: " + saveMe + ";saveMeNot: " + saveMeNot, Toast.LENGTH_LONG).show();
            }
        });
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        // save saveMe in bundle
        outState.putInt("saveMe", saveMe);
        super.onSaveInstanceState(outState);
        Log.d("TEST", "Saving saveMe in bundle during orientation change");
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        // retrieve saveMe from bundle
        saveMe = savedInstanceState.getInt("saveMe");
        Log.d("TEST", "Retrieving saveMe in bundle during orientation change");
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    tools:context="test.saveinstance.MainActivity">

    <EditText
        Android:id="@+id/withId"
        Android:layout_marginTop="30dp"
        Android:layout_width="match_parent"
        Android:layout_height="70dp"
        Android:gravity="center"
        Android:hint="Type Here (has Android:id)" />

    <EditText
        Android:layout_below="@id/withId"
        Android:layout_marginTop="20dp"
        Android:layout_width="match_parent"
        Android:layout_height="70dp"
        Android:gravity="center"
        Android:hint="Type Here (doesn't have Android:id)" />

    <Button
        Android:id="@+id/button"
        Android:layout_alignParentBottom="true"
        Android:layout_marginBottom="10dp"
        Android:text="Click Me"
        Android:layout_width="match_parent"
        Android:layout_height="50dp" />

    <Button
        Android:id="@+id/save"
        Android:layout_above="@id/button"
        Android:layout_marginBottom="10dp"
        Android:text="Save"
        Android:layout_width="match_parent"
        Android:layout_height="50dp" />

</RelativeLayout>
2
JRG