私はAndroid SDKプラットフォームに取り組んできましたが、アプリケーションの状態を保存する方法は少しわかりません。だから、 'こんにちは、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分ほどドキュメントを覗き見してきて、何も明白なことを見つけていません。
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.
}
Bundleは本質的にはNVP( "Name-Value Pair")マップを保存する方法です。そしてそれは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");
}
通常、このテクニックを使用してアプリケーションのインスタンス値(選択、未保存のテキストなど)を保存します。
savedInstanceState
は、現在のナビゲーションや選択情報など、現在のActivityのインスタンスに関連付けられた状態を保存するためのものです。AndroidがActivityを破棄して再作成した場合、元の状態に戻ることができます。 onCreate
および onSaveInstanceState
の資料を参照してください。
より長持ちするためには、SQLiteデータベース、ファイル、または設定を使用することを検討してください。 永続状態の保存 を参照してください。
この文書は次のように述べています( 'Activity Lifecycle'セクションにあります)。
onPause()
の代わりにの代わりに永続データをonSaveInstanceState(Bundle)
に保存することが重要です。後者はライフサイクルコールバックの一部ではないため、ではないからです。 は、その文書の[]に記載されているすべての状況で呼び出されました。
つまり、onPause()
とonResume()
に永続データの保存/復元コードを入れてください。
EDIT:さらに明確にするために、これはonSaveInstanceState()
のドキュメントです。
このメソッドは、アクティビティが殺される前に呼び出されるので、将来いつか戻ってきたときにその状態を復元できます。 たとえば、アクティビティBがアクティビティAの前で開始され、ある時点でアクティビティAがリソースを取り戻すために殺された場合、アクティビティAにはを節約するチャンスがあります。ユーザーがアクティビティAに戻ったときにユーザーインターフェイスの状態を
onCreate(Bundle)
またはonRestoreInstanceState(Bundle)
で復元できるように、このメソッドによるユーザーインターフェイスの現在の状態。
私の同僚は、活動のライフサイクルと状態情報、状態情報の保存方法、状態Bundle
とSharedPreferences
と ここを見てください への保存に関する説明を含む、Androidデバイスのアプリケーション状態を説明する記事を書きました。
この記事では3つのアプローチを取り上げます。
[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);
}
[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();
}
これはAndroid開発の古典的な「問題点」です。ここに2つの問題があります。
これらすべてのスレッドに目を通すと、開発者がこれら2つの異なる問題について同時に話している時間が多いのではないかと思われます。
まず、「意図された」動作を明確にするために、onSaveInstanceとonRestoreInstanceは脆弱であり、一時的な状態に限られます。意図された用法(afaict)は、電話が回転したときのActivityの再現を処理することです(向きの変更)。言い換えれば、意図された用法はあなたの活動がまだ論理的に「上に」あるが、それでもシステムによって再インスタンス化されなければならないときです。保存されたBundleはプロセス/ memory/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から)アプリを起動したときに発生するように思われるため、開発者はこれに遭遇します。しかし、いくつかのApp Storeのアップデートメカニズムを通じても同様です(つまり、ユーザーにも影響します)。
私の主な問題はこのバグであり、意図されたフレームワークの振る舞いではないことに気づく前に、私は何時間もこれらのスレッドを通して戦いました。素晴らしい記事と workaround (更新:下記参照)この回答のユーザ@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;
}
}
onSaveInstanceState
は、システムがメモリを必要とし、アプリケーションを強制終了したときに呼び出されます。ユーザーがアプリケーションを閉じただけでは呼び出されません。そのため、アプリケーションの状態もonPause
に保存する必要があると思います。Preferences
やSqlite
などの永続的なストレージに保存する必要があります
どちらの方法も便利で有効であり、どちらもさまざまなシナリオに最適です。
onSaveInstanceState()
に保存して復元します。通常はonRestoreInstanceState()
で十分です。状態データを永続的な方法で保存すると、onResume()
またはonCreate()
(または実際にはすべてのライフサイクル呼び出し)で再ロードできます。これは望ましい動作である場合も、そうでない場合もあります。それをInstanceState
のバンドルに格納する場合、それは一時的なものであり、同じユーザーの「セッション」で使用するデータの格納にのみ適しています(私は「セッション」という用語を大まかに使用します)。
1つの方法が他の方法より優れているわけではありません。すべての問題と同様に、必要な動作を理解し、最も適切な方法を選択することが重要です。
私にとっては、状態を保存することはせいぜい1つの問題です。永続データを保存する必要がある場合は、 SQLite データベースを使用してください。 AndroidはSOOOを簡単にします。
このようなもの:
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;
私は答えを見つけたと思います。私がしたことを簡単な言葉で説明しましょう。
アクティビティ1とアクティビティ2の2つのアクティビティがあり、アクティビティ1からアクティビティ2に移動し(アクティビティ2で作業をいくつか行った)、アクティビティ1のボタンをクリックして再びアクティビティ1に戻るとします。今この段階で私はactivity2に戻りたいと思い、私は最後にactivity2を去ったときと同じ状態で私のactivity2を見たいと思います。
上記のシナリオで私が行ったことは、マニフェストで次のようにいくつか変更を加えたことです。
<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を表示できます。
これが答えだと私は信じています。これは私にとってはうまくいきます。間違っていれば訂正してください。
一時データ用のonSaveInstanceState()
(onCreate()
/onRestoreInstanceState()
に復元)、永続データ用のonPause()
(onResume()
に復元) Androidの技術資料から:
onSaveInstanceState() アクティビティが停止されている場合にAndroidによって呼び出され、再開される前に強制終了される可能性があります。これは、アクティビティが再起動されたときに同じ状態に再初期化するために必要なすべての状態を格納する必要があることを意味します。これはonCreate()メソッドの対応物であり、実際にonCreate()に渡されるsavedInstanceState Bundleは、onSaveInstanceState()メソッドでoutStateとして構築したものと同じBundleです。
onPause() および onResume() も相補的なメソッドです。 onPause()は、アクティビティが終了したときに常に開始されていても、常に呼び出されます(たとえばfinish()呼び出し)。これを使って現在のメモをデータベースに保存します。 onPause()中に解放される可能性のあるリソースを解放することをお勧めします。パッシブ状態にあるときに占めるリソースは少なくなります。
アクティビティがバックグラウンドになったとき、本当にonSaveInstance
状態が落ちています
ドキュメントからの引用: "メソッドをそのようなバックグラウンド状態にする前にメソッドonSaveInstanceState(Bundle)
が呼び出されます"
ボイラープレートを減らすために、インスタンス状態を保存するために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 license の下でライセンスされている AndroidAutowire という名前のライブラリプロジェクトから修正されました。
一方、私は一般的にこれ以上使用しないでください
Bundle savedInstanceState & Co
ライフサイクルはほとんどの活動にとって非常に複雑で必要ではありません。
そしてグーグルは自信を持っている、それも信頼できない。
私の方法は、設定に変更をすぐに保存することです。
SharedPreferences p;
p.edit().put(..).commit()
ある意味ではSharedPreferencesはBundlesのように機能します。そして当然のことながら、最初はそのような値を設定から読み取る必要があります。
複雑なデータの場合は、設定を使う代わりにSQLiteを使うことができます。
この概念を適用すると、アクティビティは、最後に保存された状態をそのまま使用し続けます。これは、最初に開いた状態で、その間に再起動が行われたか、バックスタックが原因で再開されたものです。
元の質問に直接答える。 Activityが再作成されることはないため、savedInstancestateはnullです。
次の場合にのみ、アクティビティはステートバンドルで再作成されます。
メモリが圧迫されているとき、または長期間バックグラウンドにいると、Androidはバックグラウンドアクティビティを破棄します。
あなたのハローワールドの例をテストするとき、アクティビティから出て戻るためのいくつかの方法があります。
ほとんどの場合、自宅に戻ってアプリを再起動しただけでは、アクティビティを再作成する必要はありません。それはすでにメモリ内に存在するので、onCreate()は呼ばれません。
「設定」 - >「開発者向けオプション」の下には、「アクティビティを保持しない」というオプションがあります。 Androidが有効になっていると、アクティビティは常に破壊され、バックグラウンドになったときに再作成されます。これは、最悪のシナリオをシミュレートするため、開発時に有効にしておくのに最適なオプションです。 (あなたの活動を常にリサイクルしている低メモリデバイス)。
他の答えはそれらがあなたに状態を保存する正しい方法を教えるという点で価値がありますが、私は彼らがあなたのコードがあなたが期待したようにはたらいていなかった理由本当に答えなかった。
onSaveInstanceState(bundle)
メソッドとonRestoreInstanceState(bundle)
メソッドは、画面を回転させている間(データの向きを変更する)単にデータを永続化するのに役立ちます。
(onSaveInstanceState()
メソッドは呼び出されますが、onCreate(bundle)
とonRestoreInstanceState(bundle)
は再度呼び出されないため)これらはアプリケーション間の切り替え時にはさらに良くありません。
持続性を高めるには、共有設定を使用してください。 この記事を読んでください
私が抱えていた問題は、アプリケーションの存続期間(つまり、同じアプリケーション内で他のサブアクティビティを起動してデバイスを回転させるなどの1回の実行)の間だけ持続性が必要なことでした。私は上記の答えのさまざまな組み合わせを試しましたが、私がすべての状況で欲しいものを得ませんでした。結局、私にとってうまくいったのは、onCreateの間にsavedInstanceStateへの参照を取得することでした。
mySavedInstanceState=savedInstanceState;
次の行に沿って、必要に応じて変数の内容を取得するためにそれを使用します。
if (mySavedInstanceState !=null) {
boolean myVariable = mySavedInstanceState.getBoolean("MyVariable");
}
上記のようにonSaveInstanceState
とonRestoreInstanceState
を使用していますが、変数の変更時に変数を保存するために私の方法を使用することもできます(例:putBoolean
を使用)。
受け入れられた答えは正しいですが、 Icepick と呼ばれるライブラリを使用してAndroid上でActivity状態を保存するためのより速くそしてより簡単な方法があります。 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
で保存する任意のオブジェクトで動作します。
アクティビティが作成されると、onCreate()メソッドが呼び出されます。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
savedInstanceStateはBundleクラスのオブジェクトで、初めてnullになりますが、再作成時には値が含まれています。アクティビティの状態を保存するには、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を呼び出して保存します。 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");
}
この変更を実装するには、基本的に2つの方法があります。
onSaveInstanceState()
とonRestoreInstanceState()
を使います。Android:configChanges="orientation|screenSize"
に。私は本当に2番目の方法を使用することをお勧めしません。私の経験の1つでは、ポートレートからランドスケープ、またはその逆に回転すると、デバイス画面の半分が黒くなっていました。
上記の最初の方法を使用すると、方向が変更されたときや設定が変更されたときにデータを永続化することができます。
例: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);
}
これは Steve Moseley の回答( ToolmakerSteve による)からのコメントです(全体としてonSaveInstanceState対onPause、east cost対west cost saga)。
@VVK - 部分的に同意しません。アプリを終了するいくつかの方法は onSaveInstanceState(oSIS)をトリガーしません。これはoSISの有用性を制限します。最小限のOSリソースでサポートする価値はありますが、アプリがどのように終了したとしても、アプリがユーザーを元の状態に戻すことを望む場合は、それは重要です。代わりに永続的ストレージアプローチを使用する必要があります。[ - ____] バンドルをチェックするためにonCreateを使用し、それが見つからない場合は 永続的ストレージをチェックします。 これは意思決定を集中化します。私はクラッシュから回復することができます、または戻るボタンの終了またはカスタムメニュー項目の終了、またはユーザーは何日も後にスクリーンに戻ることができます。 - ツールメーカーSteve Sep 19'15 at 10:38
コトリンコード:
保存する:
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
Optionalsを使用したくない場合は、デフォルト値を追加してください。
この問題を簡単に解決するには 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はカスタムViewsのインスタンス状態コードを生成することもできます。
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
}
私の解決策が眉をひそめているかどうかわからないが、私はViewModel状態を持続させるためにバウンドサービスを使用する。サービス内のメモリに保存するか、SQLiteデータベースから永続化して取得するかは、要件によって異なります。これはどのような種類のサービスでも実行できることであり、アプリケーションの状態の維持や抽象共通ビジネスロジックなどのサービスを提供します。
モバイルデバイスに固有のメモリと処理の制約のために、私はWebページと同じようにAndroidのビューを扱います。ページは状態を維持しません。それは純粋にプレゼンテーション層コンポーネントであり、その唯一の目的はアプリケーションの状態を提示し、ユーザー入力を受け入れることです。 Webアプリケーションアーキテクチャの最近の傾向では、ページがView、ドメインデータがモデル、そしてコントローラがWebサービスの背後にある、古くからあるModel、View、Controller(MVC)パターンを使用しています。 Viewと同じパターンをAndroidで使用することもできます。View、モデルはドメインデータであり、ControllerはAndroidバウンドサービスとして実装されています。ビューがコントローラとやり取りするときはいつでも、開始/再開時にそれにバインドし、停止/一時停止時にバインド解除します。
このアプローチでは、すべてのアプリケーションビジネスロジックをサービスに移動して、複数のビューにまたがる重複ロジックを削減し、ビューでもう1つの重要な設計原則である単一責任を強制できるため、懸案事項を追加できます。
onCreate()
に保存されているアクティビティ状態データを取得するには、まずSaveInstanceState(Bundle savedInstanceState)
メソッドをオーバーライドしてsavedInstanceStateにデータを保存する必要があります。
アクティビティがSaveInstanceState(Bundle savedInstanceState)
メソッドを破棄したときに、保存したいデータをそこに保存します。アクティビティが再開されてもonCreate()
は同じになります(アクティビティが破棄される前にデータを保存しているため、savedInstanceStateはnullにはなりません)。
今すぐAndroidは ViewModels 状態を保存するために提供します、あなたはsaveInstanceStateの代わりにそれを使用しようとするべきです。
向きが変わってもなぜEditText
内のテキストが自動的に保存されるのでしょうか。まあ、この答えはあなたのためです。
アクティビティのインスタンスが破棄され、システムが新しいインスタンスを再作成した場合(設定の変更など)古いアクティビティ状態( インスタンス状態 )の保存されたデータのセットを使用してそれを再作成しようとします。
インスタンス状態は、Bundle
オブジェクトに格納された Key-Value のペアのコレクションです。
デフォルトでは、システムはビューオブジェクトをバンドルに保存します。
EditText
内のテキストListView
などで位置をスクロールする.インスタンス状態の一部として別の変数を保存する必要がある場合は、 _ override _ onSavedInstanceState(Bundle savedinstaneState)
メソッドをオーバーライドする必要があります。
たとえば、GameActivityのint currentScore
@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)
OR
onRestoreInstanceState(Bundle savedInstanceState)
どちらのメソッドも同じBundleオブジェクトを取得するので、復元ロジックをどこに書いてもかまいません。唯一の違いは、onCreate(Bundle savedInstanceState)
メソッドではnullチェックをしなければならないのに対し、後者の場合は必要ないということです。他の回答では既にコードスニペットがあります。あなたはそれらを参照することができます。
@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)
はシステムによって呼び出されません。データを保存する際は、すべてのシナリオを考慮する必要があります。
永続化したい変数を格納および取得するには、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
}
もっと良いアイデアがあります。これはonCreateを再度呼び出さずにデータを保存するのに適しています。向きが変わったときは、アクティビティから無効にすることができます。
あなたのマニフェストでは:
<activity Android:name=".MainActivity"
Android:configChanges="orientation|screenSize">