2791

私は 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分ほど調べてみましたが、明らかなことは何も見つかりませんでした。

4

36 に答える 36

2682

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.
}

onCreate()バンドルは基本的に NVP (「名前と値のペア」) マップを格納する方法であり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");
}

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

于 2008-09-30T06:12:20.847 に答える
454

savedInstanceStateは、現在のナビゲーションや選択情報など、アクティビティの現在のインスタンスに関連付けられた状態を保存するためだけのものです。これにより、Android がアクティビティを破棄して再作成した場合に、以前の状態に戻ることができます。onCreateおよびのドキュメントを参照してください。onSaveInstanceState

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

于 2008-09-30T05:03:44.133 に答える
441

Activity のドキュメントによる と、永続的なデータを使用するのは安全ではないことに注意してください。onSaveInstanceStateonRestoreInstanceState

ドキュメントの状態 (「アクティビティ ライフサイクル」セクション):

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

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

さらに明確にするために、onSaveInstanceState()ドキュメントは次のとおりです。

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

于 2010-05-25T23:22:51.547 に答える
222

私の同僚は、アクティビティのライフサイクルと状態情報、状態情報の保存方法、状態への保存方法、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();
}
于 2010-08-27T13:54:51.297 に答える
157

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

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

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

最初に、「意図された」動作を明確にします。onSaveInstance と onRestoreInstance は壊れやすく、一時的な状態でのみ使用できます。(私が知る限り)意図された使用法は、電話が回転したとき(向きが変わったとき)にアクティビティの再生成を処理することです。言い換えれば、意図された使用法は、アクティビティがまだ論理的に「最上位」にあるが、システムによって再インスタンス化する必要がある場合です。保存された Bundle は process/memory/ GCの外では永続化されないため、アクティビティがバックグラウンドに移行する場合、これに頼ることはできません。はい、おそらくあなたのアクティビティのメモリはバックグラウンドへの旅行を生き残り、GC をエスケープしますが、これは信頼できません (また、予測可能でもありません)。

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

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

基本的に、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;
    }
}
于 2012-10-19T23:47:22.343 に答える
100

onSaveInstanceStateシステムがメモリを必要とし、アプリケーションを強制終了したときに呼び出されます。ユーザーがアプリケーションを閉じるだけでは呼び出されません。したがって、アプリケーションの状態もに保存する必要があると思いますonPause

PreferencesまたはSQLiteなどの永続ストレージに保存する必要があります。

于 2010-05-07T00:21:48.930 に答える
79

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

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

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

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

于 2011-06-27T16:17:25.003 に答える
78

私の知る限り、状態を保存することはせいぜい応急修理です。永続データを保存する必要がある場合は、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;
于 2011-06-23T17:07:53.047 に答える
66

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

activity1 と activity2 の 2 つのアクティビティがあり、activity1 から activity2 に移動し (activity2 でいくつかの作業を行いました)、再び activity1 のボタンをクリックしてアクティビティ 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);

これで、アクティビティ 2 で行った変更が失われることはなく、以前と同じ状態でアクティビティ 2 を表示できます。

これが答えだと思います。これは私にとってはうまくいきます。私が間違っている場合は修正してください。

于 2012-02-05T11:35:19.087 に答える
51

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

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

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

于 2012-01-17T18:28:40.513 に答える
46

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

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

于 2010-05-23T22:32:44.110 に答える
41

その間、私は一般的にもう使用しません

Bundle savedInstanceState & Co

ライフ サイクルは、ほとんどのアクティビティにとって複雑すぎて不要です。

そしてGoogleは、信頼できるものではないと述べています。

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

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

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

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

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

于 2012-03-31T13:36:59.767 に答える
41

ボイラープレートを削減するために、以下を使用して、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という名前のライブラリ プロジェクトから改作されました。

于 2015-08-20T02:04:11.063 に答える
32

onSaveInstanceState(bundle)およびメソッドは、画面を回転させる (向きを変更する) だけでデータを永続化するのonRestoreInstanceState(bundle)に役立ちます。
アプリケーション間の切り替え中もうまくいきません (onSaveInstanceState()メソッドが呼び出されますがonCreate(bundle)onRestoreInstanceState(bundle)再度呼び出されることはないためです。
永続性を高めるには、共有設定を使用してください。この記事を読んでください)。

于 2012-09-05T08:27:49.410 に答える
23

私の問題は、アプリケーションの有効期間中のみ永続化が必要なことでした (つまり、同じアプリ内の他のサブアクティビティの開始やデバイスの回転などを含む単一の実行)。上記の回答をさまざまに組み合わせてみましたが、すべての状況で必要なものが得られませんでした。最終的に私にとってうまくいったのは、onCreate中にsavedInstanceStateへの参照を取得することでした:

mySavedInstanceState=savedInstanceState;

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

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

onSaveInstanceState上記のようにandを使用onRestoreInstanceStateしますが、変数が変更されたときに変数を保存するために、メソッドを使用することも、または代わりに使用することもできると思います (例: を使用putBoolean)

于 2014-04-16T17:11:47.937 に答える
21

受け入れられた答えは正しいですが、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.

于 2016-01-26T03:07:39.650 に答える
10

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

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

于 2016-09-28T11:46:11.060 に答える
8

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

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

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

于 2016-08-09T12:57:37.613 に答える
7

この問題を解決する簡単な方法は、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
}
于 2016-08-08T02:07:19.273 に答える