Activity 知識梳理(3) Activity狀態保存和恢復

1、概述

在開發過程當中,不可避免地會遇到Activity被回收的場景, Activity被回收有兩種狀況:主動和被動。java

  • Activity是被主動回收時,例如按下了Back鍵,那麼這時候是沒法恢復的,由於系統認爲你已經再也不須要它了。
  • 在被動回收的狀況下,雖然這個Activity的實例已經被銷燬了,可是系統在新建一個Activity實例的時候,會帶上先前被回收Activity的信息,這些信息是被存儲在Bundle的鍵值對,這裏面有些是系統幫咱們讀寫的,例如Activity當中View的狀態,這部分的信息並不須要咱們擔憂。(爲了系統能幫咱們恢復狀態,必需要爲每一個View都指定一個id),若是咱們但願保存更多臨時的信息,而這些信息又沒有必要寫入到持久化的存儲當中,這時候咱們應該重寫onSaveInstanceState方法,在該回調當中會傳入一個Bundle對象,只須要把保存的信息放到裏面,以後再在onRestoreInstanceStateonCreate方法中把它讀取出來就能夠了,這裏面有兩點須要注意的:
  • onRestoreInstance只有在Bundle不爲空時纔會回調,而在onCreate方法當中是經過判空操做來判斷是否有須要恢復的狀態的。
  • 在重寫onRestoreInstanceState方法時,應當先調用super方法,這樣由系統負責保存的部分纔可以恢復。

2、疑問

關於狀態的保存和恢復,實現方法很簡單,咱們主要了解一下它內部的原理,主要有這麼幾個問題:bash

2.1 對於View的狀態,是怎麼作到自動恢復的

因爲文檔要求咱們必須調用super方法,那麼就能夠知道保存View狀態的代碼入口必然在Activity當中,咱們看下Activity的默認實現:函數

<!-- Activity.java -->
protected void onSaveInstanceState(Bundle outState){
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState);
}

//看到mWindow,天然就想到了PhoneWindow.java
<!-- PhoneWindow.java -->
public Bundle saveHierarchyState() {
     mContentParent.saveHierarchyState(states); //這裏是保存View狀態的地方。  
     outState.putInt(FOCUSED_ID_TAG, focusedView.getId()); //保存Focus。
     outState.putSparseParcelableArray(PANELS_TAG, panelStates); //保存Panel狀態。
}

<!-- View.java -->
public void saveHierachyState(SparseArray<Parcelable> container) {
   dispatchSaveInstanceState(container);
}
protected void dispatchSaveInstceState(Parcelable> container) {
    Parcelable state = onSaveInstanceState();
    container.put(mID, state);
}
複製代碼

整個保存View狀態的流程以下:佈局

  • 調用ActivityonSaveInstanceState方法
  • 該方法又調用mWindow.saveHierarchyState,把返回的結果保存到WINDOW_HIERARCHY_TAG這個Key對應的Value
  • mWindow的實現類PhoneWindow當中:
  • 調用根佈局的saveHierarchyState方法,這裏面會從根佈局按樹形結構遍歷,調用每一個ViewGroup/ViewonSaveInstanceState
  • 保存FoucusView

同時,咱們也能夠獲得結論,保存的前提有兩個spa

  • View的子類必須實現了onSaveInstanceState
  • 它必需要有一個ID,這個ID做爲Bundlekey,這也爲咱們實現自定義 View 時,須要保存狀態提供了思路。

2.2 onSaveInstanceState調用時機

下面看下第二個問題,有了前面分析Activity生命週期的經驗,咱們直接在ActivityThread中找相關的代碼:code

<!-- ActivityThread.java -->
//在ActivityThread中,一共有3處調用了 saveInstanceState

private void handleRelaunchActivity(ActivityClientRecord tmp) {
    performPauseActivity(r.token, false, r.isPreHoneycomb());
    if (r.state == null && !r.stopped && !r.isPreHoneycomb) {
        //保存
    }
}

final Bundle performPauseActivity(ActivityRecord r, boolean finished, boolean saveState) {
   if (!r.activity.mFinished && saveState) { //這裏saveState的條件是!r.isPreHoneycomb。
       //保存
   }
   mInstrumentation.callActivityOnPause(r.activity);
}

//這個函數調用的地方有:
1.private void handleWindowVisibility(IBinder token, boolean show) //這裏saveState是false
2.public void performStopActivity(IBinder token, boolean saveState) //LocalActivityManager調過來的也是false
3.private void handleStopActivity(IBinder token, boolean show, int configChanges) //這裏是true,經過 ActivityManagerSupervisor 調過來的

private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShow, boolean saveState) {
    if (r.state == null) {
        //保存  
    }
    r.activity.performStop();
}
複製代碼

看完這三個調用的地方,問題又解決了,結論就是:orm

  • 若是是honeycomb以後的版本,那麼在performPauseActivity時是不會保存的,而對於honeycomb以前的版本,會在回調onPause()以前保存。
  • 若是在performPauseActivity時沒有保存,那麼在執行performStopActivityInnerr.state爲空而且是從 handleStopActivity過來的,那麼會在onPause()onStop()之間保存狀態,其它狀況下不會保存狀態。
  • ReLaunchAcitivity時,也會保存狀態。

2.3 onRestoreInstanceState調用時機

Activity的生命週期解析中分析onStart()方法的時候咱們已經知道,它是在onStart()onResume()之間被調用的。對象

相關文章
相關標籤/搜索