美文网首页
View源码——saveInstanceState

View源码——saveInstanceState

作者: sollian | 来源:发表于2019-01-25 15:57 被阅读17次

    基于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:

        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之前。附上一段生命周期图:

    图1

    相关文章

      网友评论

          本文标题:View源码——saveInstanceState

          本文链接:https://www.haomeiwen.com/subject/axbmjqtx.html