異常情況下Activity的生命周期
內存不足、系統參數改變(比如:屏幕旋轉)、各種國產管家衛士(百度衛士、360衛士、騰訊管家)的一鍵清理都會讓Activity被殺死,這種殺死和讓Activity正常銷毀的生命周期不同,根據 When are onSaveInstanceState() and onRestoreInstanceState() called, exactly? 中的回答:
- onSaveInstanceState Prior to Honeycomb, activities were not considered killable until after they had been paused, meaning that onSaveInstanceState() was called immediately before onPause(). Beginning with Honeycomb, however, Activities are considered to be killable only after they have been stopped, meaning that
onSaveInstanceState() will now be called before onStop() instead of immediately before onPause().- onRestoreInstanceState This method is called between onStart() and onPostCreate(Bundle) when the activity is being re-initialized from a previously saved state
繪制出在 Android 3.0 以上系統的Activity異常銷毀生命周期流程圖:
onSaveInstanceState和onRestoreInstanceState方法只有在Activity被異常殺死的時候才會被調用,那Activity狀態的保存與恢復和自定義控件的狀態保存與恢復之間是什么關系呢?
onSaveInstanceState
當Activity被異常銷毀時,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);
}
mWindow.saveHierarchyState()方法又會調用Window的saveHierarchyState()方法,PhoneWindow是Windown的唯一實現類,所以具體實現是PhoneWindow的saveHierarchyState():
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
mContentParent.saveHierarchyState(states);
outState.putSparseParcelableArray(VIEWS_TAG, states);
// Save the focused view ID.
final View focusedView = mContentParent.findFocus();
if (focusedView != null && focusedView.getId() != View.NO_ID) {
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
}
// save the panels
SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
savePanelState(panelStates);
if (panelStates.size() > 0) {
outState.putSparseParcelableArray(PANELS_TAG, panelStates);
}
if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
}
return outState;
}
該方法調用了 mContentParent.saveHierarchyState(states);來保存狀態,mContentParent是一個ViewGroup,但是ViewGroup里面沒有實現saveHierarchyState,所以會先調用它的父類View的saveHierarchyState:
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
}
mContentParent是一個ViewGroup,而ViewGroup里重寫了dispatchSaveInstanceState,所以會調用ViewGroup的dispatchSaveInstanceState:
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
super.dispatchSaveInstanceState(container);
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
View c = children[i];
if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
c.dispatchSaveInstanceState(container);
}
}
}
在setContentView的時候會把我們自己寫的布局加載到mContentParent,所以遍歷的是我們自己寫的所有View,我們再看一下View里面的dispatchSaveInstanceState:
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
Parcelable state = onSaveInstanceState();
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if (state != null) {
// Log.i("View", "Freezing #" + Integer.toHexString(mID)
// + ": " + state);
container.put(mID, state);
}
}
}
- 調用onSaveInstanceState()獲取保存的狀態
- 通過PFLAG_SAVE_STATE_CALLED來保證我門在自定義控件中重寫onSaveInstanceState的時候,要通過調用super.onSaveInstanceState來調用View的onSaveInstanceState
- 根據View的id來保存狀態,所以對于需要保存狀態的View要調用setId來設置一個id
onRestoreInstanceState(Bundle savedInstanceState)
當Activity被你重新創建時,如果Activity是被異常銷毀的,那么會調用 Activity的onRestoreInstanceState:
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
先取得銷毀時在onSaveInstanceState里面保存的數據,然后會調用Window的restoreHierarchyState,具體實現方法:
@Override
public void restoreHierarchyState(Bundle savedInstanceState) {
if (mContentParent == null) {
return;
}
SparseArray<Parcelable> savedStates
= savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
if (savedStates != null) {
mContentParent.restoreHierarchyState(savedStates);
}
// restore the focused view
int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
if (focusedViewId != View.NO_ID) {
View needsFocus = mContentParent.findViewById(focusedViewId);
if (needsFocus != null) {
needsFocus.requestFocus();
} else {
Log.w(TAG,
"Previously focused view reported id " + focusedViewId
+ " during save, but can't be found during restore.");
}
}
// Restore the panels.
SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
if (panelStates != null) {
restorePanelState(panelStates);
}
if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates =
savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
if (actionBarStates != null) {
doPendingInvalidatePanelMenu();
mDecorContentParent.restoreToolbarHierarchyState(actionBarStates);
} else {
Log.w(TAG, "Missing saved instance states for action bar views! " +
"State will not be restored.");
}
}
}
mContentParent.restoreHierarchyState(savedStates);會調用View的restoreHierarchyState:
public void restoreHierarchyState(SparseArray<Parcelable> container) {
dispatchRestoreInstanceState(container);
}
又會調用ViewGroup的dispatchRestoreInstanceState:
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
super.dispatchRestoreInstanceState(container);
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
View c = children[i];
if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
c.dispatchRestoreInstanceState(container);
}
}
}
通過調用super.dispatchRestoreInstanceState(container);先獲取自己保存的狀態,
然后遍歷調用子view的dispatchRestoreInstanceState(container)
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
Parcelable state = container.get(mID);
if (state != null) {
// Log.i("View", "Restoreing #" + Integer.toHexString(mID)
// + ": " + state);
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
onRestoreInstanceState(state);
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onRestoreInstanceState()");
}
}
}
}
根據resId先取得當前View的state,然后調用View自己的onRestoreInstanceState,如果自定義控件就會重寫onRestoreInstanceState,這里又通過檢查PFLAG_SAVE_STATE_CALLED的狀態,讓用戶通過super.onRestoreInstanceState強制調用父類View的onRestoreInstanceState:
@CallSuper
protected void onRestoreInstanceState(Parcelable state) {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (state != null && !(state instanceof AbsSavedState)) {
throw new IllegalArgumentException("Wrong state class, expecting View State but "
+ "received " + state.getClass().toString() + " instead. This usually happens "
+ "when two views of different type have the same id in the same hierarchy. "
+ "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "
+ "other views do not use the same id.");
}
if (state != null && state instanceof BaseSavedState) {
mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
}
}
實現一個具有狀態保存與恢復功能的自定義控件
代碼可以去我的Github查看:自帶清空功能的編輯框