从网上看到一篇Acitivity状态保存和恢复很好的文章,讲得很透彻和实用。
一、onSaveInstanceState 函數與onRestoreInstanceState 函數的作用
onSaveInstanceState 函數:用於保存Activity的狀態信息(UI控制項的狀態信息)和用戶保存的信息
onRestoreInstanceState函數:用於恢復Activity被系統銷毀時的狀態信息和用戶保存的信息
二、onSaveInstanceState
函數的調用時間
1、當用戶按下HOME鍵時
2、長按HOME鍵,選擇運行其他的程序時
3、按下電源按鍵(關閉屏幕顯示)時
4、
從activity
A中啟動一個新的activity時
A中啟動一個新的activity時
5、
屏幕方向切換時,例如從豎屏切換到橫屏時
- 當系統銷毀一個Activity的時候,onSaveInstanceState方法會被調用,如內存不足、用戶直接按Home鍵等,當如果是用戶按返回鍵,則不會調用onSaveInstanceState方法,因為系統覺得沒必要保存數據
- 如果我們沒有複寫onSaveInstanceState()方法, 此方法的默認實現會自動保存activity中的某些狀態數據,
比如activity中各種UI控制項的狀態
- 如果我們需要覆寫onSaveInstanceState()方法,
一般會在第一行代碼中調用該方法的默認實現:super.onSaveInstanceState(outState)三、存儲數據和恢複數據原理
- 屏幕方向切換時,Activity被系統銷毀後重新建立,此時會調用onSaveInstanceState函數保存數據,待屏幕切換完成後,會調用onCreate()方法和onRestoreInstanceState()方法方法恢複數據,具體此過程的順序如下:
- Activity銷毀之前先調用onSaveInstanceState函數保存Activity的狀態信息(saveHierarchyState 和saveAllState)和用戶保存的數據
- 調用onCreate()方法restoreAllState,恢復allstate所有狀態信息
- 調用onRestoreInstanceState()方法restoreHierarchyState恢復階級調用信息
- 我們是從何得知這個順序的呢,還得從Activity的生命周期以及使用LogCat得出,觀看下面LogCat數據,為切換屏幕方向時的LogCat信息
- 可以看到,切換屏幕時上述函數的調用順序是onPause -->onSaveInstanceState-->onStop-->onDestory-->onCreate(切換屏幕後重新創建Activity時調用的onCreate方法)-->onStart-->onRestoryInstanceState-->onResume
- 依照android官方源碼的解釋,onSaveInstanceState方法一定是在onStop方法之前調用,但不保證在onPause方法之前或之後調用,官方源碼解釋如下:If called, this method will occur before
onStop
.
There are no guarantees about whether it will occur before or afteronPause
.
- 那調用onSaveInstanceState方法保存Activity狀態數據的時候究竟是保存了哪些東西呢?查看onSaveInstanceState函數的源代碼就知道了
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
- 由上面的android源碼可以看到,其實也是保存的到Bundle中,保存的數據有兩種,
- Window 的 HierarchyState,可以理解為窗口的階級狀態,裡面包含的數據已經列印在上面的圖片中,其實也就是一些panels,一些包含ID的UI控制項,還有一些ActionBar等信息,還包括一些用戶自己保存的信息,這個下面再說到
- 用一個Paracelable 對象保存 Fragment的所有狀態,再將這個Paracelable對象添加到Bundle對象中
- 恢複數據時調用了onCreate方法,onCreate方法源代碼如下
protected void onCreate(Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
if (mLastNonConfigurationInstances != null) {
mAllLoaderManagers = mLastNonConfigurationInstances.loaders;
}
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
mEnableDefaultActionBarUp = true;
} else {
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
}
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
mCalled = true;
}
- 由上面的源代碼可以看出,onCreate方法恢複數據時是恢復了Paracelable對象下的Fragment的allstate,前提是 saveInstanceState!=null ,這個條件一般不容易滿足,只有在Activity被系統銷毀時,onCreate方法中的參數saveInstanceState才不會空,否則就算調用了onsaveInstanceState方法,onCreate方法中的參數saveInstanceState也依舊是null,這點可以從源代碼的注釋中得出
Parameters:
onSaveInstanceState
.
Note:Otherwise it is null
- 調用了onCreate方法之後,系統再調用onRestoryInstanceState方法恢複數據,onRestoryInstanceState方法的源代碼如下:
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
- 由上面的源代碼可以看出,onRestoryInstanceState函數恢復的數據是HierarchyState
- 再重新說一下onSaveInstanceState方法中保存的數據,其中有一項是UI控制項的ID,並不是所有的UI控制項的信息都會被保存,必須滿足兩個條件
- 該UI控制項有ID
- 該UI空間當前獲得焦點,即被點擊到
- 這兩個條件也可以從源代碼的注釋中得出,而且可以寫一個Demo證明這種這種解釋是對的
The default implementation takes care of most of the UI per-instance state
for you by calling
an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of
for you by calling
android.view.View.onSaveInstanceState()
on each view in the hierarchy that hasan id, and by saving the id of the currently focused view (all of which is restored by the default implementation of
onRestoreInstanceState
).- 由此可以重新總結:
- 當Activity被系統撤銷後重新建立時,保存以及恢複數據的函數調用順序是:onSaveInstanceState(保存數據)-->onCreate(恢複數據allstate)-->onRestoryInstanceState(恢複數據HierarchyState)
- onSaveInstanceState函數保存的數據時:View.HierarchyState
,即panels,UI控制項,ActionBar等
- 保存的UI控制項必須是包含ID的,而且是當前獲得焦點的UI控制項才會被保存