Fragment相关源码解析三——状态保存与恢复
来源:互联网 发布:马哥linux培训 编辑:程序博客网 时间:2024/06/08 19:37
这一篇单独来说一说Fragment的状态保存和恢复。
状态保存
只要Activity不是被主动finish的,状态的保存与恢复机制就会被触发,包括 :Activity因为内存不足被killed、其他命令杀死进程、手机配置改变导致的Activity重建(用户没有手动处理配置改变的情况下)。导致onSaveInstanceState(在onStop之前)和onRestoreInstanceState(在onStart之后)的回调。而配置的改变还会在onDestroy之前引发Activity的另一个回调onRetainNonConfigurationInstance()。这两个回调相互独立,所保存的内容也不相同。
onSaveInstanceState
onSaveInstanceState方法的参数outState是一个Bundle对象,是系统保存状态时持久化状态的容器,在该方法中会调用 Parcelable p = mFragments.saveAllState() ,Fragment的状态以Parcelable的形式保存在outState中。
//FragmentActivity.javaprotected void onSaveInstanceState(Bundle outState) { ... Parcelable p = mFragments.saveAllState(); if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p); } ...}
保存的内容
既然调用了mFragments的saveAllState(),我们看一下这个方法总体上做了什么事情。mFragments是一个FragmentController对象,它持有Activity的很多信息,负责管理Fragment,它对Fragment的控制最终也是由FragmentManagerImpl这个类实现的。方法大部分省略了,最后我们可以看到,新建了一个FragmentManagerState,并对其3个成员赋值,FragmentManagerState对象保存了FragmentManagerImpl对象中的基本信息,作为一个可序列化的对象交给系统。
//FragmentManagerImpl.javaParcelable saveAllState() { ..... FragmentManagerState fms = new FragmentManagerState(); fms.mActive = active; fms.mAdded = added; fms.mBackStack = backStack; return fms; }//FragmentManagerState.javafinal class FragmentManagerState implements Parcelable { FragmentState[] mActive; int[] mAdded; BackStackState[] mBackStack; ...}
mActive成员
看一下fms.mActive,它是一个FragmentState[](对应着FragmentManagerImpl#mActive),FragmentState和Fragment在同一个包中,实现了Parcelable接口,对应着一个Fragment实例,保存了Fragment的一些状态,我们看一下它的成员就知道,分为两类,一类是tag、arguments、mRetainInstance这样的变量,还有一类是Bundle mSavedFragmentState,它用来保存一些其他的状态。这两类信息足以代表一个Fragment的状态,FragmentState把它们都封装了起来。
//FragmentState.java final String mClassName; final int mIndex; final boolean mFromLayout; final int mFragmentId; final int mContainerId; final String mTag; final boolean mRetainInstance; final boolean mDetached; final Bundle mArguments; Bundle mSavedFragmentState; Fragment mInstance;
我们看下FragmentManagerImpl具体是怎样将Fragment的状态保存在FragmentState中的,还是在saveAllState() 方法中
- FragmentState fs = new FragmentState(f);
前面说的第一类基本变量 fs.mSavedFragmentState = saveFragmentBasicState(f);
前面说的第二类其他状态- 调用Fragment的onSaveInstanceState(保存用户自己需要保存的变量),同时将saveAllState()分发给子FragmentManager,完成了状态保存的分发。
- 保存Fragment的View树状态
- 保存了用户可见性状态mUserVisibleHint
如果f.mTarget不空,保存相关状态
//FragmentManagerImpl.java Parcelable saveAllState() { ... if (mActive == null || mActive.size() <= 0) { return null; } int N = mActive.size(); FragmentState[] active = new FragmentState[N]; ... for (int i=0; i<N; i++) { //遍历FragmentManagerImpl的mActive成员 Fragment f = mActive.get(i); if (f != null) { if (f.mIndex < 0) { throwException(new IllegalStateException(...); } ... //保存第一类变量 FragmentState fs = new FragmentState(f); active[i] = fs; // 对处于正常生命周期状态下的Fragment。 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { //保存第二类变量,以Bundle的形式,包括子Fragment状态,视图状态,mUserVisibleHint fs.mSavedFragmentState = saveFragmentBasicState(f); // 同时,如果Fragment有target,还需要保存target对应的索引index, 以及target的mTargetRequestCode if (f.mTarget != null) { if (f.mTarget.mIndex < 0) { throwException(new IllegalStateException(...); } 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; } } } ... }
mSavedFragmentState这个Bundle中保存了以下这些状态:
mAdded成员
mAdded是一个int[]型数组,被用来保存当前已经被added到FragmentManager下的所有fragments的index 。
// FragmentManagerImpl.javaParcelable saveAllState() { ... if (mAdded != null) { N = mAdded.size(); if (N > 0) { added = new int[N]; for (int i=0; i<N; i++) { // 依次遍历mAdded数组,将Fragment对应的index添加至added added[i] = mAdded.get(i).mIndex; if (added[i] < 0) { throwException(new IllegalStateException(...); } } } } ...}
mBackStack成员
BackStackState保存已经被添加到回退栈的BackStackRecord中的内容。
//BackStackState.java final int[] mOps; final int mTransition; final int mTransitionStyle; final String mName; final int mIndex; final int mBreadCrumbTitleRes; final CharSequence mBreadCrumbTitleText; final int mBreadCrumbShortTitleRes; final CharSequence mBreadCrumbShortTitleText; final ArrayList<String> mSharedElementSourceNames; final ArrayList<String> mSharedElementTargetNames;
保存了FragmentManagerImpl的mBackStack成员。
// FragmentManagerImpl.javaParcelable saveAllState() { ... if (mBackStack != null) { N = mBackStack.size(); if (N > 0) { backStack = new BackStackState[N]; //遍历FragmentManagerImpl的mBackStack并保存 for (int i=0; i<N; i++) { backStack[i] = new BackStackState(mBackStack.get(i)); if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i + ": " + mBackStack.get(i)); } } } ...}
小结
可以看到,Fragment的状态保存的触发在Activity的onSaveInstanceState 中,分别对FragmentManagerImpl中的mActive、mAdded、mBackStack成员进行了相应的保存。其中对mActive的保存,也就是对应Fragment的保存,涉及到基本变量、用户自定义保存变量、子FragmentManagerImpl需要保存的变量、视图状态变量、fragment的target等相关变量;对mAdded的保存,是把对应Fragment的index保存;对mBackStack的保存,是对对应BackStackRecord的相关变量的保存。最后以FragmentManagerState实例的形式返回。
NoConfig下的状态保存
在Manifest中未对Activity进行android:configChanges配置,称为NonConfig 。这时候如果设备配置发生改变,Activity会被销毁并重建。在这种情况下,Activity的onSaveInstanceState 依旧会被调用。但是对于Fragment,我们可能希望其中的一些工作不被中断,因此Fragment可调用setRetainInstance(true),在设备配置改变时,系统在销毁Activity前(onDestroy中)会回调Activity#onRetainNonConfigurationInstance() ,Fragment实例不会被销毁,而是被保存在了特殊的对象里交给了ActivityThread对象,也就是说Fragment的对象直接缓存在了内存中,区别于上面的将Fragment的实例状态持久化(虽然这个持久化动作依然会进行)。如果我们在mainfest中配置了Activity的android:configChanges属性,那么这个方法不会调用。
// FragmentActivity.java@Overridepublic final Object onRetainNonConfigurationInstance() { ... FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig(); ... NonConfigurationInstances nci = new NonConfigurationInstances(); ... nci.fragments = fragments; ... return nci;}
同样,也是由FragmentManagerImpl这个类实现的,将需要 保持实例的Fragment集合 及 其相应的嵌套子Fragment实例集合 都保存在了FragmentManagerNonConfig对象中并返回,FragmentActivity将得到的该对象又被包装在NonConfigurationInstances里返回给了系统。
ArrayList<Fragment> retainNonConfig() { ArrayList<Fragment> fragments = null; if (mActive != null) { for (int i=0; i<mActive.size(); i++) { Fragment f = mActive.get(i); //如果实例具有mRetainInstance标志位,这个标志位在setRetainInstance中设置 if (f != null && f.mRetainInstance) { if (fragments == null) { fragments = new ArrayList<Fragment>(); } fragments.add(f); //设置其mRetaining标志位,这个标志位将对Fragment的生命周期回调产生影响 f.mRetaining = true; f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f); } } } return fragments; }
两种状态保存的总结
保存分为两种情况:一、内存不足或进程被其他软件杀死。二、设备配置的改变。这两种情况都会触发FragmentActivity的onSaveInstanceState 回调,而在设备配置改变的情况下还会回调FragmentActivity的onRetainNonConfigurationInstance() 。这两个回调是独立的,也就是说在配置改变的情况下,这两个方法都会被回调。值得一提的是setRetainInstance(true) 若能产生效果只会是在配置发生改变的时候,若Activity只是被killed,那么这个函数起不到任何作用,实例依然会被销毁(涉及到Fragment的mRetaining字段的改变),只能通过onSaveInstanceState回调保存一些变量, 如果 setRetainInstance(false) 的话Fragment依旧只能恢复一些变量。
状态恢复
NonConfigurationInstances被重新赋值到Activity中
在Activity被create时,Activity里首次被调用的方法是attach(…):,在该方法中系统传入了缓存在ActivityTread中的lastNonConfigurationInstances
final void attach(...NonConfigurationInstances lastNonConfigurationInstances...) { ... mLastNonConfigurationInstances = lastNonConfigurationInstances; ...}
所有Fragment的状态恢复
在接下来的生命周期回调onCreate中,Activity使用了可能存在的NonConfigurationInstances实例以及一定存在的所有Fragment的持久化数据 对其所管辖的所有Fragment进行状态恢复。状态恢复过后,所有Fragment的状态都是INITIALZING,接着调用dispatchCreate() 方法进行生命周期的转移。
// FragmentActivity.java protected void onCreate(@Nullable Bundle savedInstanceState) { ... NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { mFragments.restoreLoaderNonConfig(nc.loaders); } if (savedInstanceState != null) { Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); mFragments.restoreAllState(p, nc != null ? nc.fragments : null); .... } ... mFragments.dispatchCreate(); }
mFragments.restoreAllState是状态恢复的入口,mFragments.restoreAllState最终将事件分发给了FragmentManagerImpl的restoreAllState方法,这个方法利用FragmentManagerState恢复了FragmentManagerImpl的状态。
// FragmentManagerImpl.javavoid restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) { ... FragmentManagerState fms = (FragmentManagerState)state; ... // step1 if (nonConfig != null) { ... } ... // step2 for (int i=0; i<fms.mActive.length; i++) { FragmentState fs = fms.mActive[i]; if (fs != null) { ... } } // step3 if (nonConfig != null) { ... } ... // step4 if (fms.mAdded != null) { ... } ... // step5 if (fms.mBackStack != null) { ... } ...}
(1)将内存中的Fragment实例关联至FragmentManagerState的FragmentState[]
我们知道持久化的FragmentManagerState中有一个mActive成员,它是一个FragmentState[],代表了所有的Fragment(被添加到回退栈和被添加到Activity中的),我们将缓存的Fragment实例赋值给对应的FragmentState的mInstance成员,那么在接下来的步骤就不用根据持久化的基本变量重建一个新的Fragment了。
// FragmentManager # restoreAllState()if (nonConfig != null) { List<Fragment> nonConfigFragments = nonConfig.getFragments(); childNonConfigs = nonConfig.getChildNonConfigs(); final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0; for (int i = 0; i < count; i++) { // 得到保持的实例对象 Fragment f = nonConfigFragments.get(i); // 取出实例对象在保存了状态的数组中对应的FragmentState FragmentState fs = fms.mActive[f.mIndex]; // 将实例关联到对应的FragmentState fs.mInstance = f; f.mSavedViewState = null; f.mBackStackNesting = 0; f.mInLayout = false; f.mAdded = false; f.mTarget = null; if (fs.mSavedFragmentState != null) { fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader()); f.mSavedViewState =FragmentManagerImpl.VIEW_STATE_TAG); f.mSavedFragmentState = fs.mSavedFragmentState; } }}
(2)恢复FragmentManagerImpl的mAvtive成员
由FragmentManagerState的mActive恢复对应的FragmentManagerImpl的mAvtive。
// FragmentManager # restoreAllState()for (int i=0; i<fms.mActive.length; i++) { FragmentState fs = fms.mActive[i]; if (fs != null) { FragmentManagerNonConfig childNonConfig = null; if (childNonConfigs != null && i < childNonConfigs.size()) { // 拿到Fragment的子Fragment的实例 childNonConfig = childNonConfigs.get(i); } // 该方法通过FragmentState保存的状态返回一个Fragment实例 Fragment f = fs.instantiate(mHost, mParent, childNonConfig); // 添加 mActive.add(f); fs.mInstance = null; } else { mActive.add(null); if (mAvailIndices == null) { mAvailIndices = new ArrayList<Integer>(); } if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i); mAvailIndices.add(i); }}
其中FragmentState的instantiate() 方法如下,可以看到如果前面关联了缓存的Fragment实例,就直接返回mInstance,否则利用反射的方式创建一个Fragment实例并赋值给mInstance,接着为它的成员赋值,包括mArguments等一些基本变量,mSavedFragmentState这样保存了用户自定义变量和View状态的Bundle。
// FragmentState.javapublic Fragment instantiate(FragmentHostCallback host, Fragment parent, FragmentManagerNonConfig childNonConfig) { if (mInstance == null) { final Context context = host.getContext(); if (mArguments != null) { mArguments.setClassLoader(context.getClassLoader()); } //反射 mInstance = Fragment.instantiate(context, mClassName, mArguments); if (mSavedFragmentState != null) { mSavedFragmentState.setClassLoader(context.getClassLoader()); mInstance.mSavedFragmentState = mSavedFragmentState; } mInstance.setIndex(mIndex, parent); mInstance.mFromLayout = mFromLayout; mInstance.mRestored = true; mInstance.mFragmentId = mFragmentId; mInstance.mContainerId = mContainerId; mInstance.mTag = mTag; mInstance.mRetainInstance = mRetainInstance; mInstance.mDetached = mDetached; mInstance.mHidden = mHidden; mInstance.mFragmentManager = host.mFragmentManager; } mInstance.mChildNonConfig = childNonConfig; return mInstance;}
(3)更新保存的在内存中的Fragment的mTarget成员
// FragmentManager # restoreAllState()// Update the target of all retained fragments. if (nonConfig != null) { for (int i=0; i<nonConfig.size(); i++) { Fragment f = nonConfig.get(i); if (f.mTargetIndex >= 0) { if (f.mTargetIndex < mActive.size()) { f.mTarget = mActive.get(f.mTargetIndex); } else { Log.w(TAG, "Re-attaching retained fragment " + f + " target no longer exists: " + f.mTargetIndex); f.mTarget = null; } } } }
(4)恢复FragmentManagerImpl的mAdded成员
由FragmentManagerState的mAdded恢复对应的FragmentManagerImpl的mAdded。注意FragmentManagerState中持久化的是一个int[],这里根据其已经恢复好的mActive来把FragmentManagerState中的int[]转化成FragmentManagerImpl中的ArrayList < Fragment >
// FragmentManager # restoreAllState()if (fms.mAdded != null) { // 该数组保存的是已经Add的Fragment在mActive对应是索引 mAdded = new ArrayList<Fragment>(fms.mAdded.length); for (int i=0; i<fms.mAdded.length; i++) { Fragment f = mActive.get(fms.mAdded[i]); if (f == null) { throwException(new IllegalStateException(...); } f.mAdded = true; if (mAdded.contains(f)) { throw new IllegalStateException("Already added!"); } mAdded.add(f); }} else { mAdded = null;}
(5)恢复FragmentManagerImpl的mBackStack成员
// FragmentManager # restoreAllState()if (fms.mBackStack != null) { mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); for (int i=0; i<fms.mBackStack.length; i++) { BackStackRecord bse = fms.mBackStack[i].instantiate(this); mBackStack.add(bse); if (bse.mIndex >= 0) { setBackStackIndex(bse.mIndex, bse); } }} else { mBackStack = null;}
总结
对于状态保存,在一般情况下,Fragment的状态保存属于FragmentManager的状态保存,依靠可序列化的对象FragmentManagerState,将需要保存的信息封装在内并交由系统,在配置改变的情况下,Fragment实例可能直接保存在内存中;对于状态恢复,保存在内存中的实例和可序列化的FragmentManagerState共同配合,在Activity的onCreate回调中完成其所管辖的Fragment实例的状态恢复,并在后续生命周期方法回调中对Fragment状态进行转移。
- Fragment相关源码解析三——状态保存与恢复
- Android Fragment使用(三) Activity, Fragment, WebView的状态保存和恢复
- Android状态保存与恢复流程 完全解析
- Android 状态保存与恢复流程 完全解析
- Fragment的状态保存和恢复
- Fragment相关源码解析一——FragmentManagerImpl和BackStackRecord
- Fragment相关源码解析二——生命周期
- Android状态保存与恢复
- Activity : 状态保存与恢复
- [译]Android Activity 和 Fragment 状态保存与恢复的最佳实践
- [译]Android Activity 和 Fragment 状态保存与恢复的最佳实践
- Android Activity 和 Fragment 状态保存与恢复的最佳实践
- Android Activity 和 Fragment 状态保存与恢复的最佳实践
- Fragment详解之六——如何监听fragment中的回退事件与怎样保存fragment状态
- Fragment详解之六——如何监听fragment中的回退事件与怎样保存fragment状态
- Fragment详解之六——如何监听fragment中的回退事件与怎样保存fragment状态
- Fragment详解之六——如何监听fragment中的回退事件与怎样保存fragment状态
- Fragment详解之六——如何监听fragment中的回退事件与怎样保存fragment状态
- MFC双缓冲绘图解决界面闪烁问题
- iOS APPstore 审核加速
- 数据结构之图
- mybatis笔记-多个参数查询报BindingException异常
- 数据库三大范式(Normal Form)
- Fragment相关源码解析三——状态保存与恢复
- EXTJS 开始日期和开始时间,结束日期和结束时间,当前日期和当前时间之间的比较
- Eclipse 快捷键
- SVDchapter14 机器学习之利用SVD简化数据
- 关于安卓安装包和PC安装包的反编译(破解)
- Unity中一键创建常用文件夹
- C语言宏定义用法总结
- gvim常用快捷键
- UVa 11134 Fabled Rooks ——区间选点