基于api28
setter getter
public boolean isSaveEnabled() {
return (mViewFlags & SAVE_DISABLED_MASK) != SAVE_DISABLED;
}
public void setSaveEnabled(boolean enabled) {
setFlags(enabled ? 0 : SAVE_DISABLED, SAVE_DISABLED_MASK);
}
這里對應的標志位是SAVE_DISABLED
還有兩個類似的方法:
public boolean isSaveFromParentEnabled() {
return (mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED;
}
public void setSaveFromParentEnabled(boolean enabled) {
setFlags(enabled ? 0 : PARENT_SAVE_DISABLED, PARENT_SAVE_DISABLED_MASK);
}
這里對應的標志位是PARENT_SAVE_DISABLED
兩個標志位有什么區(qū)別呢?我們來看下整個事件的傳遞過程就知道了。這里只看saveInstanceState的傳遞,restoreInstanceState是一樣的。
事件傳遞
saveInstanceState事件最早由ActivityThread發(fā)起。
ActivityThread源碼不再貼了,清楚調用時機即可:
- api < 11,onSaveInstance在onPause之前執(zhí)行
- 11 <= api < 28,onSaveInstance在onPause之后,onStop之前執(zhí)行
- api >= 28,onSaveInstance在onStop之后執(zhí)行
Activity:
//由ActivityThread調用
final void performSaveInstanceState(Bundle outState) {
onSaveInstanceState(outState);
...
}
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
...
}
這里的mWindow是PhoneWindow。
PhoneWindow:
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
//當前窗口內的view的狀態(tài)都會保存在這個SparseArray中
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
//調用view的方法,開始遍歷視圖樹
mContentParent.saveHierarchyState(states);
...
return outState;
}
View:
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
}
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
//想要保存自身狀態(tài),需要設置id,并且未設置SAVE_DISABLED_MASK標志位
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
//調用onSaveInstanceState,真正實現保存自身狀態(tài)
Parcelable state = onSaveInstanceState();
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if (state != null) {
// 最終放到這里保存
container.put(mID, state);
}
}
}
@CallSuper
@Nullable protected Parcelable onSaveInstanceState() {
...
}
ViewGroup:
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
//先調用super.dispatchSaveInstanceState來保存自身的狀態(tài)
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) {
//未設置PARENT_SAVE_DISABLED才會傳遞給c,
//否則,c及其子View都不會收到saveInstanceState事件
c.dispatchSaveInstanceState(container);
}
}
}
結論如下:
- setSaveEnabled(boolean enabled)僅僅影響自身,不會影響其子View
- setSaveFromParentEnabled(boolean enabled)同時影響自身及其子View
- 要真正實現保存自身狀態(tài),還需要為View設置id
- View的id作為SparseArray中的鍵值,還必須在視圖樹中保證唯一性,否則會造成混亂
關于第四點,特意試驗了一下,id相同的兩個view,在onSaveInstance中保存自己的hashCode,然后在onRestoreInstanceState讀取出來,結果如下:
2019-01-25 15:30:02.385 8499-8499/com.sollian.sourcestudy D/MyTextView: onSaveInstanceState: 73681134
2019-01-25 15:30:02.386 8499-8499/com.sollian.sourcestudy D/MyTextView: onSaveInstanceState: 50315663
2019-01-25 15:30:03.389 8499-8499/com.sollian.sourcestudy D/MyTextView: onRestoreInstanceState: 50315663
2019-01-25 15:30:03.389 8499-8499/com.sollian.sourcestudy D/MyTextView: onRestoreInstanceState: 50315663
可以看到,第一個view的數據被后面的覆蓋了。
最后,onRestoreInstanceState的調用時機在onStart之后,onResume之前。附上一段生命周期圖:

圖1