天天看點

Android應用開發—onSaveInstanceState方法什麼時候被調用?

轉載自 onSaveInstanceState方法什麼時候被調用?(轉載/整理)
  • 在 Activity 被銷毀之前被調用來儲存每個執行個體的狀态,這樣就可以保證該狀态能夠從 onCreate(Bundle) 或者onRestoreInstanceState(Bundle)恢複過來。 本方法在 Activity 可能被銷毀前調用,這樣當該 Activity 在将來某個時刻重新回來時可以恢複其之前的狀态。例如,如果 Activity B 啟用後位于 Activity A 的前端,在某個時刻 Activity A 因為系統回收資源的原因要被銷毀,Activity A 有機會通過 onSaveInstanceState() 來儲存其使用者界面狀态,使得将來使用者傳回到 Activity A 的時候能夠通過 onCreate(Bundle) 或者onRestoreInstanceState(Bundle) 來恢複其界面狀态。不要将這個方法和 Activity 生命周期中的回調如 onPause() 或 onStop() 搞混淆了,onPause() 在 Activtiy 被放置到背景或者自行銷毀時總會被調用,onStop() 在 Activity 被銷毀時被調用。一個會調用 onPause() 和 onStop() 但不會觸發 onSaveInstanceState() 的例子是當使用者從 Activity B 傳回到 Activity A 時:沒有必要調用 B 的 onSaveInstanceState(Bundle)方法,因為此時的 B 執行個體永遠不會被恢複,是以系統會避免調用它。一個調用 onPause() 但不調用 onSaveInstanceState(Bundle) 方法的例子是當 Activity B 啟動後處在 Activity A 的前端:如果在B的整個生命周期裡 A 的使用者界面狀态都沒有被破壞的話,系統是不會調用 Activity A 的onSaveInstanceState(Bundle)方法。預設的實作負責了大部分 UI 執行個體狀态的儲存,采用的方式是調用 UI 層上每個擁有 id 的 view 的 onSaveInstanceState()方法 ,并且儲存目前獲得焦點的 view 的 id (所有儲存的狀态資訊都會在預設的 onRestoreInstanceState(Bundle) 實作中恢複)。如果你覆寫這個方法來儲存額外的沒有被各個view儲存的資訊,你可能想要在預設實作過程中調用或者自己儲存每個視圖的所有狀态。如果被調用,這個方法會在 onStop() 前被觸發,但系統并不保證是否在 onPause() 之前或者之後觸發。
  • Activity 中的 onSaveInstanceState() 方法和 onRestoreInstanceState() 方法并不是生命周期方法,它們不同于 onCreate()、onPause() 等生命周期方法,它們并不一定會被觸發。當應用遇到意外情況(如:記憶體不足、使用者直接按Home鍵),由系統銷毀一個 Activity 時,onSaveInstanceState() 方法就會被調用。但是當使用者主動去銷毀一個 Activity 時,例如在應用中按傳回鍵,onSaveInstanceState() 方法就不會被調用。因為在這種情況下,使用者的行為決定了不需要儲存Activity的狀态。通常onSaveInstanceState() 方法隻适合用于儲存一些臨時性的狀态,而onPause() 方法适合用于資料的持久化儲存。 另外,當螢幕的方向發生了改變, Activity 會被銷毀并重新建立,如果你想在 Activity 被銷毀前緩存一些資料,并且在 Activity 被重新建立後恢複緩存的資料。可以重寫 Activity 中的 onSaveInstanceState() 方法和 onRestoreInstanceState()方法,如下:
    public class PreferencesActivity extends Activity {
      private String name;
      protected void onRestoreInstanceState(Bundle savedInstanceState) {
          // 重新建立後恢複緩存的資料
          name = savedInstanceState.getString("name");
          super.onRestoreInstanceState(savedInstanceState);
      }
      protected void onSaveInstanceState(Bundle outState) {
          // 被銷毀前緩存一些資料
          outState.putString("name", "l_yqing");
          super.onSaveInstanceState(outState);
      }
    }            
  • 當使用者啟動一個新 Activity 之後,之前的 Activity 可能在記憶體中處于停止狀态也可能由于新 Activity 需要更多記憶體而被系統銷毀了,但不論怎樣,當使用者在新 Activity 上點選傳回鍵時,他希望看到的是原先的 Activity 的界面。原先的 Activity 如果是被重新建立的,那麼它就要恢複到使用者最後看到它時的樣子,我們該怎麼做呢?其實也不難,在 onPause() 、onStop() 或 onDestroy() 中儲存必要的資料就行了。但是現在Google又冒出一個新的東西:onSaveInstanceState(),觀其名可知其意:它是專門用來儲存執行個體狀态的,這個“執行個體”不是指的 Activity 對象,而是它所在的程序,因為Activity 的銷毀是因為它所在的程序被殺掉而造成的。onSaveInstanceState()是在系統感覺需要銷毀Activity時調用的,它被傳入一個參數Bundle,這個Bundle可以被認為是個 Map 字典之類的東西,用“鍵-值”的形式來儲存資料。現在又叫人蛋疼了:不是可以在 onPause() 中儲存資料嗎?為什麼又搞出這樣一個家夥來?它們之間是什麼關系呢? 原來,onSaveInstanceState() 方法的主要目的是儲存和 Activity 的狀态有關的資料,當系統在銷毀 Activity 時,如果它希望 Activity 下次出現的樣子跟之前完全一樣,那麼它就會調用onSaveInstanceState(),否則就不調用。是以要明白這一點:onSaveInstanceState() 方法并不是永遠都會調用。比如,當使用者在一個 Activity 點選傳回鍵時,就不會調用,因為使用者此時明确知道這個 Activity 是要被銷毀的,并不期望下次它的樣子跟現在一樣(當然開發者可以使它保持臨死時的表情,你非要這樣做,系統也沒辦法),是以就不用調用onSaveInstanceState()。現在應該明白了:在onPause()、onStop() 以及 onDestroy() 中需要儲存的是那些需要永久化的資料,而不是儲存用于恢複狀态的資料,狀态資料有專門的方法:onSaveInstanceState()。資料儲存在一個 Bundle 中,Bundle 被系統永久化。當再調用 Activity 的onCreate()時,原先儲存的 Bundle就被傳入,以恢複上一次臨死時的模樣,如果上次被銷毀時沒有儲存 Bundle,則為 null。 還沒完呢,如果你沒有實作自己的 onSaveInstanceState(),但是 Activity 上控件的樣子可能依然能被儲存并恢複。原來 Activity 類已實作了onSaveInstanceState(),在 onSaveInstanceState() 的預設實作中,會調用所有控件的相關方法,把控件們的狀态都儲存下來,比如 EditText 中輸入的文字、CheckBox 是否被選中等等。然而不是所有的控件都能被儲存,這取決于你是否在 layout 檔案中為控件賦了一個名字(android:id)。有名的就存,無名的不管。既然有現成的可用,那麼我們到底還要不要自己實作 onSaveInstanceState() 方法呢?這就得看情況了,如果你自己的派生類中有變量影響到UI,或你程式的行為,當然就要把這個變量也儲存了,那麼就需要自己實作,否則就不需要,但大多數情況肯定需要自己實作一下下了。對了,别忘了在你的實作中調用父類的 onSaveInstanceState() 方法。

    注:由于 onSaveInstanceState() 方法并不是在每次被銷毀時都會調用,是以不要在其中儲存那些需要永久化的資料,執行儲存那些資料的最好地方是在 onPause() 方法中。

轉自heiguy的專欄onSaveInstanceState和onRestoreInstanceState觸發的時機

先看Application Fundamentals上的一段話:

Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action (such as pressing the BACK key) 從這句話可以知道,當某個activity變得“容易”被系統銷毀時,該activity的onSaveInstanceState就會被執行,除非該activity是被使用者主動銷毀的,例如當使用者按BACK鍵的時候。 注意上面的雙引号,何為“容易”?言下之意就是該activity還沒有被銷毀,而僅僅是一種可能性。這種可能性有哪些?通過重寫一個activity的所有生命周期的onXXX方法,包括onSaveInstanceState和onRestoreInstanceState方法,我們可以清楚地知道當某個activity(假定為activity A)顯示在目前task的最上層時,

其onSaveInstanceState方法會在什麼時候被執行,有這麼幾種情況:

  • 當使用者按下HOME鍵時。 這是顯而易見的,系統不知道你按下HOME後要運作多少其他的程式,自然也不知道activity A是否會被銷毀,故系統會調用onSaveInstanceState,讓使用者有機會儲存某些非永久性的資料。以下幾種情況的分析都遵循該原則
  • 長按HOME鍵,選擇運作其他的程式時。
  • 按下電源按鍵(關閉螢幕顯示)時。
  • 從activity A中啟動一個新的activity時。
  • 螢幕方向切換時,例如從豎屏切換到橫屏時。

在螢幕切換之前,系統會銷毀activity A,在螢幕切換之後系統又會自動地建立activity A,是以onSaveInstanceState一定會被執行 總而言之,onSaveInstanceState的調用遵循一個重要原則,即當系統“未經你許可”時銷毀了你的activity,則onSaveInstanceState會被系統調用,這是系統的責任,因為它必須要提供一個機會讓你儲存你的資料(當然你不儲存那就随便你了)。

至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成對的被調用的,onRestoreInstanceState被調用的前提是,activity A“确實”被系統銷毀了,而如果僅僅是停留在有這種可能性的情況下,則該方法不會被調用,例如,當正在顯示activity A的時候,使用者按下HOME鍵回到主界面,然後使用者緊接着又傳回到activity A,這種情況下activity A一般不會因為記憶體的原因被系統銷毀,故activity A的onRestoreInstanceState方法不會被執行

另外,onRestoreInstanceState的bundle參數也會傳遞到onCreate方法中,你也可以選擇在onCreate方法中做資料還原。

繼續閱讀