View的onSaveInstanceState和onRestoreInstanceState

日常的开发过程中难免会自定义一些view,view的状态数据保存及恢复也是需要考虑进内的,就会用到onSaveInstanceState和onRestoreInstanceState这两个方法。

onSaveInstanceState

顾名思义,保存实例状态。以CompoundButton为例:

    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.checked = isChecked();
        return ss;
    }

该方法返回的是一个序列化对象,即SavedState,checked状态也是保存在该对象里面,看一下该类的实现:

    static class SavedState extends BaseSavedState {
        boolean checked;

        /**
         * Constructor called from {@link CompoundButton#onSaveInstanceState()}
         */
        SavedState(Parcelable superState) {
            super(superState);
        }

        /**
         * Constructor called from {@link #CREATOR}
         */
        private SavedState(Parcel in) {
            super(in);
            checked = (Boolean)in.readValue(null);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeValue(checked);
        }

        @Override
        public String toString() {
            return "CompoundButton.SavedState{"
                    + Integer.toHexString(System.identityHashCode(this))
                    + " checked=" + checked + "}";
        }

        @SuppressWarnings("hiding")
        public static final Parcelable.Creator CREATOR =
                new Parcelable.Creator() {
            @Override
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

代码简单,整个类即是序列化的实现,该类继承BaseSavedState,BaseSavedState在View里面定义,用于保存View的状态。BaseSavedState继承AbsSavedState,AbsSavedState则实现了Parcelable。代码就不贴了,在我们的自定义view里面,比葫芦画瓢,也实现一个继承BaseSavedState的类,把需要保存的状态利用该类保存即可。

onRestoreInstanceState

顾名思义,恢复实例状态。还是看CompoundButton的代码:

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        setChecked(ss.checked);
        requestLayout();
    }

onSaveInstanceState返回的是SavedState对象,该方法会将SavedState对象传递过来,强制类型转换之后,先将父View的状态恢复过去,再从该对象里面拿到之前保存的状态进行恢复即可。
以上即是自定义view的状态保存的基本用法,到这里还不够,我们需要阅读一下源码,看一下如上两个方法是如何调用的,才能知其然,遇到问题时知道如何分析。

源码分析

所有的view的根view均是android.view.View,可以从这里下手看看上面两个方法是怎么调用的。调用onSaveInstanceState的代码如下:


    /**
     * Store this view hierarchy's frozen state into the given container.
     *
     * @param container The SparseArray in which to save the view's state.
     *
     * @see #restoreHierarchyState(android.util.SparseArray)
     * @see #dispatchSaveInstanceState(android.util.SparseArray)
     * @see #onSaveInstanceState()
     */
    public void saveHierarchyState(SparseArray container) {
        dispatchSaveInstanceState(container);
    }

    /**
     * Called by {@link #saveHierarchyState(android.util.SparseArray)} to store the state for
     * this view and its children. May be overridden to modify how freezing happens to a
     * view's children; for example, some views may want to not store state for their children.
     *
     * @param container The SparseArray in which to save the view's state.
     *
     * @see #dispatchRestoreInstanceState(android.util.SparseArray)
     * @see #saveHierarchyState(android.util.SparseArray)
     * @see #onSaveInstanceState()
     */
    protected void dispatchSaveInstanceState(SparseArray 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是由dispatchSaveInstanceState调用的,dispatchSaveInstanceState是由saveHierarchyState调用的。onSaveInstanceState被调用的时候有两个必要的条件,只有这两个条件同时成立,onSaveInstanceState才会真正被执行,所以在实际的开发过程中并不是单纯的实现onSaveInstanceState即可,具体情况如下:

  1. mId即view的id如果为空要如何处理(如代码动态添加view或xml里面生明view时没有赋值id)
  2. (mViewFlags & SAVE_DISABLED_MASK) != 0

解决第一种情况可以代码或者xml文件里面主动给view赋值id,第二种情况默认情况下是等于0的,不主动设置save_disabled的状态即可(setSaveEnabled(false)或者android:saveEnabled="false")。
调用onRestoreInstanceState的代码如下:


    /**
     * Restore this view hierarchy's frozen state from the given container.
     *
     * @param container The SparseArray which holds previously frozen states.
     *
     * @see #saveHierarchyState(android.util.SparseArray)
     * @see #dispatchRestoreInstanceState(android.util.SparseArray)
     * @see #onRestoreInstanceState(android.os.Parcelable)
     */
    public void restoreHierarchyState(SparseArray container) {
        dispatchRestoreInstanceState(container);
    }

    /**
     * Called by {@link #restoreHierarchyState(android.util.SparseArray)} to retrieve the
     * state for this view and its children. May be overridden to modify how restoring
     * happens to a view's children; for example, some views may want to not store state
     * for their children.
     *
     * @param container The SparseArray which holds previously saved state.
     *
     * @see #dispatchSaveInstanceState(android.util.SparseArray)
     * @see #restoreHierarchyState(android.util.SparseArray)
     * @see #onRestoreInstanceState(android.os.Parcelable)
     */
    protected void dispatchRestoreInstanceState(SparseArray 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()");
                }
            }
        }
    }

从上述的调用过程可以发现,每个view的state都是存储在SparseArray container里面的,存放和获取state的key值是mID,所以mID是一定不能为空的。

View的saveHierarchyState和restoreHierarchyState又是谁调用的呢?我们先从View的父布局ViewGroup里面找一下源码吧。
ViewGroup是继承自View的,从源码里看ViewGroup并没有重写saveHierarchyState和restoreHierarchyState的,但是重写了dispatchSaveInstanceState和dispatchRestoreInstanceState,代码如下:

    @Override
    protected void dispatchSaveInstanceState(SparseArray 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);
            }
        }
    }


    @Override
    protected void dispatchRestoreInstanceState(SparseArray 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);
            }
        }
    }

由此可见所有的子View的dispatch方法均是由父布局ViewGroup来分发的,并且这些View是共享同一个SparseArray的,每个View的state都是存储在这个SparseArray里面的,key值为mID(即View的id)。如果View的id重复的话,就会导致SparseArray里面的state值被覆盖,state恢复的时候就会出现问题了。解决这种问题一种方案是使每个View的id保持唯一,但做起来会很难控制,另一种方案也是这里要介绍的方案,即使state的存储颗粒化,每个ViewGroup单独管理它的子View的state。具体实现如下:

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState savedState = new SavedState(superState);
        savedState.childrenStates = new SparseArray();
        //保存子view的state
        for (int i = 0; i < getChildCount(); i++) {
            getChildAt(i).saveHierarchyState(savedState.childrenStates);
        }
        return savedState;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState savedState = (SavedState) state;
        super.onRestoreInstanceState(savedState.getSuperState());
        //恢复子view的state
        for (int i = 0; i < getChildCount(); i++) {
            getChildAt(i).restoreHierarchyState(savedState.childrenStates);
        }
    }

    @Override
    protected void dispatchSaveInstanceState(SparseArray container) {
        //只保存自身的state到共有的SparseArray
        dispatchFreezeSelfOnly(container);
    }

    @Override
    protected void dispatchRestoreInstanceState(SparseArray container) {
        //从共有的SparseArray拿到state并只恢复自身
        dispatchThawSelfOnly(container);
    }

    public static class SavedState extends BaseSavedState {
        //用于保存子view的state
        SparseArray childrenStates;

        public SavedState(Parcel source) {
            super(source);
        }

        private SavedState(Parcel source, ClassLoader loader) {
            super(source);
            childrenStates = source.readSparseArray(loader);
        }

        public SavedState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeSparseArray(childrenStates);
        }

        public static final ClassLoaderCreator CREATOR = new ClassLoaderCreator() {
            @Override
            public SavedState createFromParcel(Parcel source, ClassLoader loader) {
                return new SavedState(source, loader);
            }

            @Override
            public SavedState createFromParcel(Parcel source) {
                return new SavedState(source);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

这里给出从Activity处开始调用onSaveInstanceState和onRestoreInstanceStatede 代码执行流程。

Activity. onSaveInstanceState
    /**
     * The hook for {@link ActivityThread} to save the state of this activity.
     *
     * Calls {@link #onSaveInstanceState(android.os.Bundle)}
     * and {@link #saveManagedDialogs(android.os.Bundle)}.
     *
     * @param outState The bundle to save the state to.
     */
    final void performSaveInstanceState(Bundle outState) {
        //调用入口
        onSaveInstanceState(outState);
        //调用activity所管理的dialog的onSaveInstanceState逻辑,从window开始流程基本一致
        saveManagedDialogs(outState);
        。。。
    }

    protected void onSaveInstanceState(Bundle outState) {
        //调用window的onSaveInstanceState
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        //保存所有fragment的状态
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mAutoFillResetNeeded) {
            outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
            getAutofillManager().onSaveInstanceState(outState);
        }
        getApplication().dispatchActivitySaveInstanceState(this, outState);
    }
Window. saveHierarchyState

Activity的mWindow是PhoneWindow的实例,所以需要看PhoneWindow的saveHierarchyState。

    @Override
    public Bundle saveHierarchyState() {
        Bundle outState = new Bundle();
        if (mContentParent == null) {
            return outState;
        }

        SparseArray states = new SparseArray();
        //mContentParent即为id为content的那层FrameLayout,setContentView的布局文件即是添加到这个ViewGroup里面的,接下来就回到了上面讲述的流程里面了
        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 panelStates = new SparseArray();
        savePanelState(panelStates);
        if (panelStates.size() > 0) {
            outState.putSparseParcelableArray(PANELS_TAG, panelStates);
        }
        //保存actionbar的状态
        if (mDecorContentParent != null) {
            SparseArray actionBarStates = new SparseArray();
            mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
            outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
        }

        return outState;
    }

再来看一下onRestoreInstanceState的流程。

Activity. onRestoreInstanceState
    /**
     * The hook for {@link ActivityThread} to restore the state of this activity.
     *
     * Calls {@link #onSaveInstanceState(android.os.Bundle)} and
     * {@link #restoreManagedDialogs(android.os.Bundle)}.
     *
     * @param savedInstanceState contains the saved state
     */
    final void performRestoreInstanceState(Bundle savedInstanceState) {
        //调用入口
        onRestoreInstanceState(savedInstanceState);
        //恢复dialog的状态
        restoreManagedDialogs(savedInstanceState);
    }

    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        if (mWindow != null) {
            Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
            if (windowState != null) {
                mWindow.restoreHierarchyState(windowState);
            }
        }
    }
Window.restoreHierarchyState
    @Override
    public void restoreHierarchyState(Bundle savedInstanceState) {
        if (mContentParent == null) {
            return;
        }
        //恢复所有view的状态
        SparseArray 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 panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
        if (panelStates != null) {
            restorePanelState(panelStates);
        }
        //恢复actionBar的状态
        if (mDecorContentParent != null) {
            SparseArray 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.");
            }
        }
    }

你可能感兴趣的:(View的onSaveInstanceState和onRestoreInstanceState)