基于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
两个标志位有什么区别呢?我们来看下整个事件的传递过程就知道了。这里只看saveInstanceState的传递,restoreInstanceState是一样的。
事件传递
saveInstanceState事件最早由ActivityThread发起。
ActivityThread源码不再贴了,清楚调用时机即可:
- api < 11,onSaveInstance在onPause之前执行
- 11 <= api < 28,onSaveInstance在onPause之后,onStop之前执行
- api >= 28,onSaveInstance在onStop之后执行
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的状态都会保存在这个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) {
//想要保存自身状态,需要设置id,并且未设置SAVE_DISABLED_MASK标志位
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
//调用onSaveInstanceState,真正实现保存自身状态
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来保存自身的状态
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
- 要真正实现保存自身状态,还需要为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
网友评论