在開發過程當中,不可避免地會遇到Activity
被回收的場景, Activity
被回收有兩種狀況:主動和被動。java
Activity
是被主動回收時,例如按下了Back
鍵,那麼這時候是沒法恢復的,由於系統認爲你已經再也不須要它了。Activity
的實例已經被銷燬了,可是系統在新建一個Activity
實例的時候,會帶上先前被回收Activity
的信息,這些信息是被存儲在Bundle
的鍵值對,這裏面有些是系統幫咱們讀寫的,例如Activity
當中View
的狀態,這部分的信息並不須要咱們擔憂。(爲了系統能幫咱們恢復狀態,必需要爲每一個View
都指定一個id
),若是咱們但願保存更多臨時的信息,而這些信息又沒有必要寫入到持久化的存儲當中,這時候咱們應該重寫onSaveInstanceState
方法,在該回調當中會傳入一個Bundle
對象,只須要把保存的信息放到裏面,以後再在onRestoreInstanceState
和onCreate
方法中把它讀取出來就能夠了,這裏面有兩點須要注意的:onRestoreInstance
只有在Bundle
不爲空時纔會回調,而在onCreate
方法當中是經過判空操做來判斷是否有須要恢復的狀態的。onRestoreInstanceState
方法時,應當先調用super
方法,這樣由系統負責保存的部分纔可以恢復。關於狀態的保存和恢復,實現方法很簡單,咱們主要了解一下它內部的原理,主要有這麼幾個問題:bash
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
狀態的流程以下:佈局
Activity
的onSaveInstanceState
方法mWindow.saveHierarchyState
,把返回的結果保存到WINDOW_HIERARCHY_TAG
這個Key
對應的Value
中mWindow
的實現類PhoneWindow
當中:saveHierarchyState
方法,這裏面會從根佈局按樹形結構遍歷,調用每一個ViewGroup/View
的onSaveInstanceState
FoucusView
同時,咱們也能夠獲得結論,保存的前提有兩個spa
View
的子類必須實現了onSaveInstanceState
ID
,這個ID
做爲Bundle
的key
,這也爲咱們實現自定義 View 時,須要保存狀態提供了思路。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
時沒有保存,那麼在執行performStopActivityInner
時r.state
爲空而且是從 handleStopActivity
過來的,那麼會在onPause()
和onStop()
之間保存狀態,其它狀況下不會保存狀態。ReLaunchAcitivity
時,也會保存狀態。onRestoreInstanceState
調用時機在Activity
的生命週期解析中分析onStart()
方法的時候咱們已經知道,它是在onStart()
和onResume()
之間被調用的。對象