Android自定义控件源码分析1——Activity被异常销毁时自定义控件状态的保存与恢复

异常情况下Activity的生命周期

内存不足、系统参数改变(比如:屏幕旋转)、各种国产管家卫士(百度卫士、360卫士、腾讯管家)的一键清理都会让Activity被杀死,这种杀死和让Activity正常销毁的生命周期不同,根据 When are onSaveInstanceState() and onRestoreInstanceState() called, exactly? 中的回答:

  • onSaveInstanceState Prior to Honeycomb, activities were not considered killable until after they had been paused, meaning that onSaveInstanceState() was called immediately before onPause(). Beginning with Honeycomb, however, Activities are considered to be killable only after they have been stopped, meaning that
    onSaveInstanceState() will now be called before onStop() instead of immediately before onPause().
  • onRestoreInstanceState This method is called between onStart() and onPostCreate(Bundle) when the activity is being re-initialized from a previously saved state

绘制出在 Android 3.0 以上系统的Activity异常销毁生命周期流程图:

Android自定义控件源码分析1——Activity被异常销毁时自定义控件状态的保存与恢复_第1张图片
异常生命周期.png

onSaveInstanceState和onRestoreInstanceState方法只有在Activity被异常杀死的时候才会被调用,那Activity状态的保存与恢复和自定义控件的状态保存与恢复之间是什么关系呢?

onSaveInstanceState

当Activity被异常销毁时,Activity的onSaveInstanceState会被调用

protected void onSaveInstanceState(Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        getApplication().dispatchActivitySaveInstanceState(this, outState);
    }

mWindow.saveHierarchyState()方法又会调用Window的saveHierarchyState()方法,PhoneWindow是Windown的唯一实现类,所以具体实现是PhoneWindow的saveHierarchyState():

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

        SparseArray states = new SparseArray();
        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);
        }

        if (mDecorContentParent != null) {
            SparseArray actionBarStates = new SparseArray();
            mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
            outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
        }

        return outState;
    }

该方法调用了 mContentParent.saveHierarchyState(states);来保存状态,mContentParent是一个ViewGroup,但是ViewGroup里面没有实现saveHierarchyState,所以会先调用它的父类View的saveHierarchyState:

    public void saveHierarchyState(SparseArray container) {
        dispatchSaveInstanceState(container);
    }

mContentParent是一个ViewGroup,而ViewGroup里重写了dispatchSaveInstanceState,所以会调用ViewGroup的dispatchSaveInstanceState:

@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);
            }
        }
    }

在setContentView的时候会把我们自己写的布局加载到mContentParent,所以遍历的是我们自己写的所有View,我们再看一下View里面的dispatchSaveInstanceState:

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);
            }
        }
    }
  1. 调用onSaveInstanceState()获取保存的状态
  2. 通过PFLAG_SAVE_STATE_CALLED来保证我门在自定义控件中重写onSaveInstanceState的时候,要通过调用super.onSaveInstanceState来调用View的onSaveInstanceState
  3. 根据View的id来保存状态,所以对于需要保存状态的View要调用setId来设置一个id

onRestoreInstanceState(Bundle savedInstanceState)

当Activity被你重新创建时,如果Activity是被异常销毁的,那么会调用 Activity的onRestoreInstanceState:

protected void onRestoreInstanceState(Bundle savedInstanceState) {
        if (mWindow != null) {
            Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
            if (windowState != null) {
                mWindow.restoreHierarchyState(windowState);
            }
        }
    }

先取得销毁时在onSaveInstanceState里面保存的数据,然后会调用Window的restoreHierarchyState,具体实现方法:

 @Override
    public void restoreHierarchyState(Bundle savedInstanceState) {
        if (mContentParent == null) {
            return;
        }

        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);
        }

        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.");
            }
        }
    }

mContentParent.restoreHierarchyState(savedStates);会调用View的restoreHierarchyState:

public void restoreHierarchyState(SparseArray container) {
        dispatchRestoreInstanceState(container);
    }

又会调用ViewGroup的dispatchRestoreInstanceState:

    @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);
            }
        }
    }

通过调用super.dispatchRestoreInstanceState(container);先获取自己保存的状态,
然后遍历调用子view的dispatchRestoreInstanceState(container)

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()");
                }
            }
        }
    }

根据resId先取得当前View的state,然后调用View自己的onRestoreInstanceState,如果自定义控件就会重写onRestoreInstanceState,这里又通过检查PFLAG_SAVE_STATE_CALLED的状态,让用户通过super.onRestoreInstanceState强制调用父类View的onRestoreInstanceState:

@CallSuper
    protected void onRestoreInstanceState(Parcelable state) {
        mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
        if (state != null && !(state instanceof AbsSavedState)) {
            throw new IllegalArgumentException("Wrong state class, expecting View State but "
                    + "received " + state.getClass().toString() + " instead. This usually happens "
                    + "when two views of different type have the same id in the same hierarchy. "
                    + "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "
                    + "other views do not use the same id.");
        }
        if (state != null && state instanceof BaseSavedState) {
            mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
        }
    }

实现一个具有状态保存与恢复功能的自定义控件

代码可以去我的Github查看:自带清空功能的编辑框

你可能感兴趣的:(Android自定义控件源码分析1——Activity被异常销毁时自定义控件状态的保存与恢复)