View源码——saveInstanceState

基于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源码不再贴了,清楚调用时机即可:

  1. api < 11,onSaveInstance在onPause之前执行
  2. 11 <= api < 28,onSaveInstance在onPause之后,onStop之前执行
  3. 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:

扫描二维码关注公众号,回复: 5181152 查看本文章
    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);
            }
        }
    }

结论如下:

  1. setSaveEnabled(boolean enabled)仅仅影响自身,不会影响其子View
  2. setSaveFromParentEnabled(boolean enabled)同时影响自身及其子View
  3. 要真正实现保存自身状态,还需要为View设置id
  4. 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之前。附上一段生命周期图:

4272648-28820ef5591c5d34.png
图1

猜你喜欢

转载自blog.csdn.net/weixin_34029949/article/details/87346793