说到状态保存,就不得不提到Activity的onSaveInstanceState()方法,这个是大家经常用到的一个函数,就是当我们的Activity被置为后台,当我们再次进入这个Activity的时候,这个Activity需要被恢复,并且回调这个方法。

下面来看看这个方法

private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";protected void onSaveInstanceState(Bundle outState) {    // 1、对Window里面的View树进行状态保存    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());    // 2、对Fragmet进行状态保存    Parcelable p = mFragments.saveAllState();    if (p != null) {        outState.putParcelable(FRAGMENTS_TAG, p);    }    getApplication().dispatchActivitySaveInstanceState(this, outState);}

可以看到上面分为两个部分:

1、对View树进行状态保存

2、对Fragment进行状态保存

因为我们的Activity的视图是由View层次树或者Fragment构成。

一、View树状态保存

我们来看看mWindow.saveHierarchyState()方法。它对应的是PhoneWindow.saveHierarchyState()。

@Overridepublic Bundle saveHierarchyState() {    Bundle outState = new Bundle();    if (mContentParent == null) {        return outState;    }    // 1、创建一个状态数组,所有view的状态都存放在这个状态数组中    SparseArray states = new SparseArray();    mContentParent.saveHierarchyState(states);    outState.putSparseParcelableArray(VIEWS_TAG, states);    // save the focused view id    View focusedView = mContentParent.findFocus();    if (focusedView != null) {        if (focusedView.getId() != View.NO_ID) {            outState.putInt(FOCUSED_ID_TAG, focusedView.getId());        } else {            if (false) {                Log.d(TAG, "couldn't save which view has focus because the focused view "                        + focusedView + " has no id.");            }        }    }    // save the panels    SparseArray panelStates = new SparseArray();    savePanelState(panelStates);    if (panelStates.size() > 0) {        outState.putSparseParcelableArray(PANELS_TAG, panelStates);    }    if (mActionBar != null) {        SparseArray actionBarStates = new SparseArray();        mActionBar.saveHierarchyState(actionBarStates);        outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);    }    return outState;}

重点代码如下:

SparseArray<Parcelable> states = new SparseArray<Parcelable>();mContentParent.saveHierarchyState(states);outState.putSparseParcelableArray(VIEWS_TAG, states);

我们主要来看看mContentParent.saveHierarchyState(states);mContentParent是整个Activity中View视图的顶层视图。它是一个ViewGroup类型。ViewGroup里面没有实现saveHierarchyState方法,它继承自View。

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

下面来看看ViewGroup中的dispatchSaveInstanceState方法。

@Overrideprotected void dispatchSaveInstanceState(SparseArray container) {   // 1、调用父类的dispatchSaveInstanceState方法,就是调用View的dispatchSaveInstanceState方法   // 目的是保存ViewGroup的当前状态    super.dispatchSaveInstanceState(container);    // 2、遍历ViewGroup的子View,调用每个子View的dispatchSaveInstanceState方法    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);        }    }}

上面就是首先保存自己的当前状态,然后进行递归保存所有子View的当前状态

下面看看View的dispatchSaveInstanceState方法

protected void dispatchSaveInstanceState(SparseArray container) {    if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {        mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;        //1、获取当前View需要保存的Parcelable        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);            // 2、将该View的state保存到SparseArray类型的container            // container就是之前创建的states,存储方式是以该view的id作为key            container.put(mID, state);        }    }}

从这里我们知道,如果一个view需要保存它的当前状态,就必须给这个view一个id,因为它将作为key来存放该view的状态。

另外,如果我们需要保存一个View的当前状态,我们可以重写onSaveInstanceState方法,把需要保存的内容进行保存进可以了。

二、View树状态保存恢复
视图树保存下来的SparsesArray不是被ViewGroup自己持有,而是整个视图树状态保存之后会放到一个Bundle中。所以View树状态恢复首先就是需要拿到这个Bundle对象,在Activity的onCreate(Bundle)和onRestoreInstanceState(Bundle)我们都可以拿到这个对象。

下面来看看Activity的onRestoreInstanceState(Bundle)方法。

protected void onRestoreInstanceState(Bundle savedInstanceState) {    if (mWindow != null) {        // 1、得到保存的状态        Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);        if (windowState != null) {            // 2、状态恢复            mWindow.restoreHierarchyState(windowState);        }    }}

下面来看看PhoneWindow的restoreHierarchyState函数

@Overridepublic 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);    }    if (mActionBar != null) {        SparseArray actionBarStates =                savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);        if (actionBarStates != null) {            mActionBar.restoreHierarchyState(actionBarStates);        } else {            Log.w(TAG, "Missing saved instance states for action bar views! " +                    "State will not be restored.");        }    }}

重点代码:

SparseArray savedStates        = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);if (savedStates != null) {    mContentParent.restoreHierarchyState(savedStates);}

ViewGroup没有重写restoreHierarchyState方法,它继承子View,下面来看看View的restoreHierarchyState代码,

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

下面看看ViewGroup的dispatchRestoreInstanceState方法。

@Overrideprotected void dispatchRestoreInstanceState(SparseArray container) {    // 1、执行父类的dispatchRestoreInstanceState方法,也就是View的dispatchRestoreInstanceState方法    // 目的是恢复当前ViewGroup的状态    // 2、递归遍历所有的子View来恢复所有子View的状态    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的dispatchRestoreInstanceState方法。

protected void dispatchRestoreInstanceState(SparseArray container) {    if (mID != NO_ID) {        // 1、得到这个View保存的状态        Parcelable state = container.get(mID);        if (state != null) {            // Log.i("View", "Restoreing #" + Integer.toHexString(mID)            // + ": " + state);            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;            // 2、回调onRestoreInstanceState进行状态恢复            onRestoreInstanceState(state);            if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {                throw new IllegalStateException(                        "Derived class did not call super.onRestoreInstanceState()");            }        }    }}

所以如果我们的一个View重写了onSaveInstanceState()方法进行了状态的保存,相应的就应该重写onRestoreInstanceState方法进行相应的状态恢复。

三、Fragment状态保存

上面我们在Activity的onSaveInstanceState方法中执行了mFragments.saveAllState()进行了Fragment的状态保存,mFragments是一个FragmentManagerImpl类型的对象,我们来看看它的saveAllState方法。

Parcelable saveAllState() {    // First collect all active fragments.    int N = mActive.size();    FragmentState[] active = new FragmentState[N];    boolean haveFragments = false;    for (int i=0; iget(i);        if (f != null) {            if (f.mIndex < 0) {                throwException(new IllegalStateException(                        "Failure saving state: active " + f                        + " has cleared index: " + f.mIndex));            }            haveFragments = true;            FragmentState fs = new FragmentState(f);            active[i] = fs;            if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {                // 重点方法就是执行saveFragmentBasicState来保存Fragment的状态                fs.mSavedFragmentState = saveFragmentBasicState(f);                if (f.mTarget != null) {                    if (fs.mSavedFragmentState == null) {                        fs.mSavedFragmentState = new Bundle();                    }                    putFragment(fs.mSavedFragmentState,                            FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);                    if (f.mTargetRequestCode != 0) {                        fs.mSavedFragmentState.putInt(                                FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,                                f.mTargetRequestCode);                    }                }            } else {                fs.mSavedFragmentState = f.mSavedFragmentState;            }        }    }    int[] added = null;    BackStackState[] backStack = null;    // Build list of currently added fragments.    if (mAdded != null) {        N = mAdded.size();        if (N > 0) {            added = new int[N];            for (int i=0; iget(i).mIndex;            }        }    }    // Now save back stack.    if (mBackStack != null) {        N = mBackStack.size();        if (N > 0) {            backStack = new BackStackState[N];            for (int i=0; inew BackStackState(this, mBackStack.get(i));                if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i                        + ": " + mBackStack.get(i));            }        }    }    FragmentManagerState fms = new FragmentManagerState();    fms.mActive = active;    fms.mAdded = added;    fms.mBackStack = backStack;    return fms;}

下面我们来看看saveFragmentBasicState方法

Bundle saveFragmentBasicState(Fragment f) {    Bundle result = null;    if (mStateBundle == null) {        mStateBundle = new Bundle();    }    f.performSaveInstanceState(mStateBundle);    if (!mStateBundle.isEmpty()) {        result = mStateBundle;        mStateBundle = null;    }    // 因为Fragment里面View视图也是View树    // 下面就是保存Fragment中的View树的状态    if (f.mView != null) {        saveFragmentViewState(f);    }    if (f.mSavedViewState != null) {        if (result == null) {            result = new Bundle();        }        result.putSparseParcelableArray(                FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);    }    if (!f.mUserVisibleHint) {        if (result == null) {            result = new Bundle();        }        // Only add this if it's not the default value        result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);    }    return result;}

下面来看看saveFragmentViewState方法

void saveFragmentViewState(Fragment f) {    if (f.mView == null) {        return;    }    if (mStateArray == null) {        mStateArray = new SparseArray();    } else {        mStateArray.clear();    }    // 这里应该比较熟悉了,跟上面一样就是对这个View树的状态进行递归遍历保存    f.mView.saveHierarchyState(mStateArray);    if (mStateArray.size() > 0) {        f.mSavedViewState = mStateArray;        mStateArray = null;    }}

四、Fragment状态恢复

我们来看看Activity的onCreate(Bundle)方法

protected void onCreate(@Nullable Bundle savedInstanceState) {    if (savedInstanceState != null) {        // 得到保存的状态        Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);        // 状态恢复        mFragments.restoreAllState(p, mLastNonConfigurationInstances != null                ? mLastNonConfigurationInstances.fragments : null);    }}

可以看到它调用的是mFragments.restoreAllState方法,mFragments是一个FragmentManagerImpl类型的对象,我们来看看它的restoreAllState方法。

参考文章:Android 视图树&View状态保存

欢迎关注微信公众号:DroidMind
精品内容独家发布平台


呈现与博客不一样的技术干货

更多相关文章

  1. Android(安卓)ListView理解,BaseAdapter
  2. 关于android工程下不能运行java main程序的解决办法
  3. Android(安卓)Fragment动态创建时replace()和add()方法的区别
  4. 如何在Android中点击overlay弹出带尾巴的气泡
  5. android图像绘制(一)多种方法做图像镜像
  6. MediaPlayer的使用 带有seekBar
  7. Android(安卓)Studio自带模版:抽屉(DrawerLayout)布局
  8. Android(安卓)聚焦拍照和震动
  9. Android自定义dialog向Activity传递数据

随机推荐

  1. android studio 导入module作为lib使用
  2. Android(安卓)GPS架构分析(一)
  3. Android使用Fragment来实现TabHost的功能
  4. Android(安卓)View之基础介绍(Android开
  5. Android(安卓)6.0系统读写文件出现FileNo
  6. 关于xml中使用ImageView或ImageButton引
  7. Android定时器之Handler的postDelyed方法
  8. [置顶] activity配置信息详解
  9. Android图片与内存优化
  10. Android中ImageView使用网络图片资源的方