Android onSaveInstanceState()、onRestoreInstanceState()儲存和恢復被系統銷燬的資料

bby發表於2018-10-16

Android系統的回收機制會在未經使用者主動操作的情況下銷燬activity,而為了避免系統回收activity導致資料丟失,Android為我們提供了onSaveInstanceState(Bundle outState)和onRestoreInstanceState(Bundle savedInstanceState)用於儲存和恢復資料。

一、onSaveInstanceState(Bundle outState)在什麼時機會被呼叫呢?

答案是當activity有可能被系統回收的情況下,而且是在onStop()之前。注意是有可能,如果是已經確定會被銷燬,比如使用者按下了返回鍵,或者呼叫了finish()方法銷燬activity,則onSaveInstanceState不會被呼叫。 或者也可以說,此方法只有在activity被異常終止的情況下會被呼叫。

總結下,onSaveInstanceState(Bundle outState)會在以下情況被呼叫:

1、當使用者按下HOME鍵時。 
2、從最近應用中選擇執行其他的程式時。 
3、按下電源按鍵(關閉螢幕顯示)時。 
4、從當前activity啟動一個新的activity時。 
5、螢幕方向切換時(無論豎屏切橫屏還是橫屏切豎屏都會呼叫)。
複製程式碼

在前4種情況下,當前activity的生命週期為:

onPause -> onSaveInstanceState -> onStop。
(這個是我測試的結果,但是根據《Android開發藝術探索》,說onPause和onSaveInstanceState的順序是不一定的)
複製程式碼

二、onRestoreInstanceState什麼時機被呼叫?

onRestoreInstanceState(BundlesavedInstanceState)只有在activity確實是被系統回收,重新建立activity的情況下才會被呼叫。

比如第5種情況螢幕方向切換時,activity生命週期如下: onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume 在這裡onRestoreInstanceState被呼叫,是因為螢幕切換時原來的activity確實被系統回收了,又重新建立了一個新的activity。 (順便吐槽一下網上的那些文章說橫屏切豎屏和豎屏切橫屏時activity生命週期方法執行不一樣,經自己實踐證明是一樣的。)

而按HOME鍵返回桌面,又馬上點選應用圖示回到原來頁面時,activity生命週期如下: onPause -> onSaveInstanceState -> onStop -> onRestart -> onStart -> onResume 因為activity沒有被系統回收,因此onRestoreInstanceState沒有被呼叫。

如果onRestoreInstanceState被呼叫了,則頁面必然被回收過,則onSaveInstanceState必然被呼叫過。

三、onCreate()裡也有Bundle引數,可以用來恢復資料,它和onRestoreInstanceState有什麼區別?

因為onSaveInstanceState 不一定會被呼叫,所以onCreate()裡的Bundle引數可能為空,如果使用onCreate()來恢復資料,一定要做非空判斷。

而onRestoreInstanceState的Bundle引數一定不會是空值,因為它只有在上次activity被回收了才會呼叫。

而且onRestoreInstanceState是在onStart()之後被呼叫的。有時候我們需要onCreate()中做的一些初始化完成之後再恢復資料,用onRestoreInstanceState會比較方便。下面是官方文件對onRestoreInstanceState的說明:

This method is called after onStart() when the activity is being re-initialized from a previously saved state, given here in savedInstanceState. Most implementations will simply use onCreate(Bundle) to restore their state, but it is sometimes convenient to do it here after all of the initialization has been done or to allow subclasses to decide whether to use your default implementation.

注意這個說明的最後一句是什麼意思? 
to allow subclasses to decide whether to use your default implementation.

它是說,用onRestoreInstanceState方法恢復資料,你可以決定是否在方法裡呼叫父類的onRestoreInstanceState方法,即是否呼叫super.onRestoreInstanceState(savedInstanceState); 
而用onCreate()恢復資料,你必須呼叫super.onCreate(savedInstanceState); 
複製程式碼

//儲存和恢復資料
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
//可以把要儲存的靜態全域性變數先轉成Json
        savedInstanceState.putBoolean("MyBoolean", true);
        savedInstanceState.putDouble("myDouble", 1.9);
        savedInstanceState.putInt("MyInt", 1);
        savedInstanceState.putString("MyString", "Welcome back to Android");
        // etc.
        super.onSaveInstanceState(savedInstanceState);
}

  @Override
  public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
// 從savedInstanceState中恢復資料,如果沒有需要恢復資料savedInstanceState為nul 
      if (savedInstanceState != null) {
        boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
        double myDouble = savedInstanceState.getDouble("myDouble");
        int myInt = savedInstanceState.getInt("MyInt");
        String myString = savedInstanceState.getString("MyString");
      }
     }
//或在onRestoreInstanceState恢復資料
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
        double myDouble = savedInstanceState.getDouble("myDouble");
        int myInt = savedInstanceState.getInt("MyInt");
        String myString = savedInstanceState.getString("MyString");
}
複製程式碼

相關文章