Android(安卓)View状态保存
说到状态保存,就不得不提到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状态保存
更多相关文章
- Android(安卓)ListView理解,BaseAdapter
- 关于android工程下不能运行java main程序的解决办法
- Android(安卓)Fragment动态创建时replace()和add()方法的区别
- 如何在Android中点击overlay弹出带尾巴的气泡
- android图像绘制(一)多种方法做图像镜像
- MediaPlayer的使用 带有seekBar
- Android(安卓)Studio自带模版:抽屉(DrawerLayout)布局
- Android(安卓)聚焦拍照和震动
- Android自定义dialog向Activity传递数据