【Fragment】对Fragment的状态保存恢复机制原理的分析

如同Activity、View等,状态的保存与恢复的重要性不言而喻,对于Fragment,在哪setRetainInstance,在什么时机save/restore State我们也早已熟练于心,但是为什么要这样、或者还有没有更多的skill却是稍显不足,也许探究一下整个机制的过程说不定就会恍然大悟。

在深入流程分析之前,我们需要明白状态的保存机制发生的前提:只要Activity不是被主动finish的,状态的保存与恢复机制就会被触发,导致onSaveInstanceState和onRestoreInstanceState的回调,包括了内存不足被killed、其它命令杀死进程等场景,甚至于手机的配置改变引发的Activity重建的情况(用户没有手动处理配置改变的情况下)。

saveAllState

Fragment依附于Activity,因此Fragment的状态保存与恢复机制也是由Activity的相关方法触发。Activity的方法onSaveInstanceState的参数outState是系统在状态需要保存时用来提供存放持久化状态的容器,当系统触发状态保存时,在该方法中,通过对mFragments的saveAllState方法的调用,Activity下的Fragment的所有状态便保存在了 FRAGMENTS_TAG 键中:

1
2
3
4
5
6
7
8
protected void onSaveInstanceState(Bundle outState) {
...
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
...
}

但是saveAllState()具体保存了Fragment的哪些状态却不从得知。我们继续分析:
mFragments对象对应的类FragmentController是供Host(Activity)调用的一层FragmentManager的包装,FragmentManager下维护的所有Fragment的生命周期都是Host通过FragmentController来转发回调的,因此saveAllState()事件也被分发到了FragmentManager的saveAllState()中:

1
2
3
4
5
6
7
8
Parcelable saveAllState() {
...
FragmentManagerState fms = new FragmentManagerState();
fms.mActive = active;
fms.mAdded = added;
fms.mBackStack = backStack;
return fms;
}

可以看到,该方法将所有需要保存的状态归结为3种并封装到了一个FragmentManagerState可序列化对象中,我们依次来看看这三种状态分别是什么以及其具体的保存过程。

fms.mActive

mActive是一个FragmentState[]数组对象,而FragmentState类则是一个实现了Parcelable接口的实体,它负责对Fragment的基本变量进行序列化,包括了arguments、tag等信息,具体保存了哪些状态如下所示(字段代表的意义可以参考Fragment的字段详解):

1
2
3
4
5
6
7
8
9
10
11
12
public FragmentState(Fragment frag) {
mClassName = frag.getClass().getName();
mIndex = frag.mIndex;
mFromLayout = frag.mFromLayout;
mFragmentId = frag.mFragmentId;
mContainerId = frag.mContainerId;
mTag = frag.mTag;
mRetainInstance = frag.mRetainInstance;
mDetached = frag.mDetached;
mArguments = frag.mArguments;
mHidden = frag.mHidden;
}

除了以上字段外,FragmentState还预留了一个Bundle mSavedFragmentState来存储一些额外的状态,具体保存了什么接下来再分析;可见,这些字段足以代表了Fragment的实例基本状态。

因此可以简单说FragmentState就是对Fragment的基本状态的一个封装。

在需要save state时,只需要将对应的Fragment通过FragmentState构造方法传入,该Fragment实例的基本状态也就封装好了,在FragmentManager中具体实现过程如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// FragmentManager
Parcelable saveAllState() {
...
// 若没有Fragment,就无需保存状态
if (mActive == null || mActive.size() <= 0) {
return null;
}
int N = mActive.size();
FragmentState[] active = new FragmentState[N];
...
for (int i=0; i<N; i++) {
Fragment f = mActive.get(i);
if (f != null) {
if (f.mIndex < 0) {
throwException(new IllegalStateException(...);
}
...
// 重点:遍历“活着”的Fragment,依次封装状态并存入active数组中。
FragmentState fs = new FragmentState(f);
active[i] = fs;
// 对处于正常生命周期状态下的Fragment,
// 还需要确保其与用户相关操作的变量以及视图层级等状态得到保存
if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
// saveFragmentBasicState具体实现了以下操作:
// 1. 执行了Fragment的onSaveInstanceState方法;
// 1.1. 同时若存在子Fragment,也对子Fragment相关状态调用saveAllState进行了保存;
// 2. 调用saveFragmentViewState保存了视图层级状态;
// 3. 保存了用户可见性状态mUserVisibleHint;
fs.mSavedFragmentState = saveFragmentBasicState(f);
if (f.mTarget != null) {
if (f.mTarget.mIndex < 0) {
throwException(new IllegalStateException(...);
}
if (fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = new Bundle();
}
// 同时,如果Fragment有target,还需要保存target对应的索引index
putFragment(fs.mSavedFragmentState, FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
// 以及target的mTargetRequestCode
if (f.mTargetRequestCode != 0) {
fs.mSavedFragmentState.putInt(FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, f.mTargetRequestCode);
}
}
} else {
// 最后,将mSavedFragmentState信息保存在FragmentState的额外字段mSavedFragmentState中;(有误)
fs.mSavedFragmentState = f.mSavedFragmentState;
}
}
}
...
}

也就是说,最终除了Fragment的一些基本状态得到保存外,上面提到的额外字段mSavedFragmentState还存储了以下信息:

信息(Bundle’s Key) 备注
TARGET_STATE_TAG target的index
TARGET_REQUEST_CODE_STATE_TAG target的requestCode
VIEW_STATE_TAG 视图层级状态
USER_VISIBLE_HINT_TAG mUserVisibleHint字段
FragmentActivity.FRAGMENTS_TAG 子FragmentManager下所维护的所有Fragment的状态
onSaveInstanceState(outState) 供用户手动保存的状态

fms.mAdded

mAdded是一个int[]型数组,被用来保存当前已经被added到FragmentManager下的所有fragments的index:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// FragmentManager#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(...);
}
}
}
}
...

fms.mBackStack

mBackStack也是一个数组,实现了Parcelable接口的BackStackState类是对FragmentManager下的回退栈backStack的状态的一个保存。我们可以看看它保存了哪些状态:

1
2
3
4
5
6
7
8
9
10
11
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;

每个字段所表示的意思可以参见BackStack类的字段详解。这里额外说明一下其中的int[] mOps的变量,它的每一小段(长度为bse.mNumOp*7 + numRemoved)表示的是一个回退栈中的单个事务:image

上图中index为6的元素如果为0,表示该事务不存在removed元素,所以该段长度就只有7;反之长度为7 + numRemoved。

由于Op类并没有实现Parcelable接口,所以这里在保存Op对象时,对其进行了“扁平”转化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// BackStackState#BackStackState(BackStackRecord bse)
...
mOps = new int[bse.mNumOp*7 + numRemoved];
...
op = bse.mHead;
int pos = 0;
while (op != null) {
mOps[pos++] = op.cmd;
mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
mOps[pos++] = op.enterAnim;
mOps[pos++] = op.exitAnim;
mOps[pos++] = op.popEnterAnim;
mOps[pos++] = op.popExitAnim;
if (op.removed != null) {
final int N = op.removed.size();
mOps[pos++] = N;
for (int i=0; i<N; i++) {
mOps[pos++] = op.removed.get(i).mIndex;
}
} else {
mOps[pos++] = 0;
}
op = op.next;
}

这样使得mOps的每个元素都是一个基本变量,可以方便地被持久化。

issue: mStateSaved

这里还有一个issue,具体可以参考Fragment字段详解中的mStateSaved说明。

意思是在Honeycomb(11)之前,save state是发生在onPause之前的,但是从版本11开始,save state的时机被修改为onPause之后了。因为对于Fragment,在pause后和stop前之间改变fragment的mstate是被允许的,因此对于老设备,为了使其和现在的逻辑保存一致(v4的兼容性),就没有在此处将mStateSaved置为true以防止在执行事务时检测到保存状态时机已经发生过了而抛出异常;

这也就意味着,在版本11之前,在pause后stop之前执行Fragment事务,虽然不发生异常但存在状态丢失的风险。

NoConfig下的状态保存

除了前面提到的一些“正常的”条件下被killed的情况下的状态保存调用之外,还有一种情况就是:

设备的配置改变引发Activity被re-created,即Activity立马被杀死并重新创建。

我们知道,若不需要自行处理Activity在配置的变更下的变更(即在Manifest中未对Activity进行android:configChanges配置,所以称为NonConfig),Activity会被销毁并重建。在这种情况下,按正常流程处理,依附于Activity的Fragment也是会按照前面的保存状态流程被销毁然后恢复的。但是对于Fragment,我们可能会经常遇到这样的情况:其中存在某些处理过程不需要因此中断,例如线程,因此Fragment提供了一个叫做setRetainInstance(true)的方法来告知系统:在宿主Activity被销毁重建的过程中,该Fragment不需要随之也销毁重建,即需要保持实例。

上述特性可以在Fragment的onCreate中开启。一旦开启,在设备配置改变时,系统在销毁Activity前会回调Activity#onRetainNonConfigurationInstance(),用来保留一些可以传递到新Activity的实例,在FragmentActivity中该方法被重写:

1
2
3
4
5
6
7
8
9
10
11
12
// FragmentActivity
@Override
public final Object onRetainNonConfigurationInstance() {
...
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
...
NonConfigurationInstances nci = new NonConfigurationInstances();
...
nci.fragments = fragments;
...
return nci;
}

mFragments.retainNestedNonConfig()最终将事件分发到了FragmentManager#retainNonConfig方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// FragmentManager
FragmentManagerNonConfig retainNonConfig() {
ArrayList<Fragment> fragments = null;
ArrayList<FragmentManagerNonConfig> childFragments = null;
...
for (int i=0; i<mActive.size(); i++) {
...
// 重要标识:仅当Fragment调用了setRetainInstance(true),该实例才会被缓存在fragments中
if (f.mRetainInstance) {
if (fragments == null) {
fragments = new ArrayList<Fragment>();
}
fragments.add(f);
// 开启该标识,以防止在moveToState时的部分生命周期方法的调用
f.mRetaining = true;
...
}
...
// 当然,其嵌套子Fragments如果也开启了mRetainInstance,也会被保存
if (f.mChildFragmentManager != null) {
FragmentManagerNonConfig child = f.mChildFragmentManager.retainNonConfig();
...
childFragments.add(child);
...
}
...
}
// 如果该FragmentManager下没有任何Fragment需要保持实例,则返回null
if (fragments == null && childFragments == null) {
return null;
}
// 最后,将缓存包装在FragmentManagerNonConfig对象中返回
return new FragmentManagerNonConfig(fragments, childFragments);
}

代码中的注释已经很详细了,纵观整个方法的流程,其实就是将需要 保持实例的Fragment集合 及 其相应的嵌套子Fragment实例集合 都保存在了FragmentManagerNonConfig对象中并返回,FragmentActivity将得到的该对象又被包装在NonConfigurationInstances里返回给了系统。
这样,在设备配置改变时,通过setRetainInstance来告知需要保持实例的Fragment对象就在Activity的被销毁过程中被保存了下来。
image

当然,如果我们在mainfest中配置了Activity的android:configChanges属性,即我们需要手动处理设备配置改变下的变化,那么也就意味着Activity不需要默认地被销毁重建,这样也就不存在Fragment保持实例这一说法了。

注意

上面分析了触发状态的保存机制的两种大的情况:分别是普通情况下例如内存不足、被其它软件杀死等,以及特殊情况——设备的配置改变。不论是哪一种情况,都会触发onSaveInstance方法的回调,即使是当设备配置改变时已经通过setRetainInstance告知了保留实例,这里需要特别注意。

并且,两种情况保存状态/实例的时机和位置是独立,也就是说onSaveInstance并不会因为setRetainInstance发生改变、或者retainNonConfig不会干扰setRetainInstance的执行和结果。

同时另一个需要注意的点就是:setRetainInstance起效的前提是设备的配置改变 这一情况,如果只是普通情况Activity被killed,Fragment依然会被销毁和稍后的恢复,即无法保持实例的,这时候只能通过onSaveInstance来保留一些状态。

restoreAllState

当Activity re-created时(包括了稍后用户手动从历史栈中回来或者配置改变引发的重建的情况),当前状态就应该恢复到上次被销毁时保存的状态。

我们知道,在Activity被create时,Activity里首次被调用的方法是attach(…):

1
2
3
4
5
final void attach(...NonConfigurationInstances lastNonConfigurationInstances...) {
...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}

在该方法中系统传入了上次包装实例的缓存对象lastNonConfigurationInstances (若存在,我们这里针对的就是存在的情况),然后在经历生命周期过程第一个方法onCreate时,Activity直接使用了该对象来恢复Fragments实例(针对.app.Fragment),在FragmentActivity中也是一样(针对.app.v4.Fragment):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Activity#onCreate
...
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.fragments : null);
}
---------------------------------------------------------------------
// FragmentActivity#onCreate
...
// getLastNonConfigurationInstance方法取就是传入Activity中的mLastNonConfigurationInstances对象
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
...
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
...
}

在存在savedInstanceState的前提下,在onCreate时直接依据FRAGMENTS_TAG键得到与Fragment相关的状态(若存在),然后调用了mFragments.restoreAllState方法,我们可以看到,传入的参数除了Fragment的状态之外,还包括了NonConfigurationInstances.fragments对象,该对象正是前面设备配置改变时,系统在销毁Activity前回调的Activity#onRetainNonConfigurationInstance()方法中保留下来的Fragment实例集合。也就是说,不论是那种触发状态保存机制的情况下保留的状态,最终在Activity re-created时,都会通过mFragments.restoreAllState入口将状态恢复。mFragments.restoreAllState最终将事件分发给了FragmentManager#restoreAllState方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// FragmentManager
void 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) {
...
}
...
}

该方法的流程可以大致分为5个步骤,FragmentManager通过这几个步骤恢复了自身的一些状态,也就恢复了所辖的所有Fragment。

Step 1:将保持的Fragment实例关联至FragmentState

由于可能存在保持了实例的Fragment,所以在该步骤中,需要将这些实例关联到上次保存的Fragment状态对象FragmentState中,这样在接下来的步骤中就不需要实例化对应的Fragment对象了(因为下面的步骤会依据保存的状态来实例化新的Fragment对象):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 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;
}
}
}

Step2:恢复FragmentManager#mActive

在将保存的状态和保持的实例合并后,接下来就需要开始遍历fms.mActive,将保存的Fragment状态依次恢复:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 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);
// 将(重新实例化)的Fragment对象关联到mActive数组中
mActive.add(f);
// 清空mInstance字段,以防最后再次从中恢复
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);
}
}

其中的fs.instantiate(mHost, mParent, childNonConfig)方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// FragmentState
public Fragment instantiate(FragmentHostCallback host, Fragment parent, FragmentManagerNonConfig childNonConfig) {
// mInstance是上一步关联进来的保持了实例的Fragment对象
if (mInstance == null) {
final Context context = host.getContext();
if (mArguments != null) {
mArguments.setClassLoader(context.getClassLoader());
}
// 最终通过Fragment的instantiate方法得到了一个全新的Fragment对象
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;
}

Fragment.instantiate(…)方法会根据所给的class name加载对应的Class类,调用clazz.newInstance()新建一个全新的Fragment对象:

1
2
3
4
5
6
7
8
9
public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
...
Fragment f = (Fragment)clazz.newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments = args;
}
...
}

得到Fragment对象后,我们还发现紧接下来第一件事就是为之绑定mArguments(若存在),所以如果需要传递一些简单的或者受限于生命周期的可持久化的参数,推荐使用setArguments(Bundle)方式。

另外,这里需要显式地强调一点:虽然在上面的FragmentState#instantiate方法中,保持了实例的Fragment的mSavedFragmentState字段并没有被赋值,但是状态的恢复机制依然与正常的流程一样。因为保持了实例就意味着该实例下的状态也会被保持。

Step3:关联恢复的Fragment的target

关于Target字段是什么可以参考Fragment的字段详解。

保存了的状态对应的Fragment实例恢复后,这一步就主要根据Fragment对应的target索引,来讲索引替换为对应的Fragment实例,当然,target索引对应的实例存在的前提是该target之前使用了Fragment#setRetainInstance机制来保持实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (nonConfig != null) {
List<Fragment> nonConfigFragments = nonConfig.getFragments();
final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
for (int i = 0; i < count; i++) {
Fragment f = nonConfigFragments.get(i);
if (f.mTargetIndex >= 0) {
if (f.mTargetIndex < mActive.size()) {
f.mTarget = mActive.get(f.mTargetIndex);
} else {
f.mTarget = null;
}
}
}
}

从这一步我们也可以知道,Fragment对应的Target在状态的保存与恢复机制下,默认只会保留target的索引index,如果target正好启用了setRetainInstance特性,那么target就会被关联至相应的Fragment。

Step4:恢复FragmentManager#mAdded

接下来,开始恢复mAdded,FragmentManager#mAdded字段主要作用就是记录已经add到Activity上的Fragment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if (fms.mAdded != null) {
// 依据的就是在saveAllState时保存的fms.mAdded数组
// 该数组保存的是已经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;
}

Step5:恢复FragmentManager#mBackStack

与第4步一样,这里通过在saveAllState时保存的fms.mBackStack数组依次恢复FragmentManager的回退栈中的BackStackRecord信息:

1
2
3
4
5
6
7
8
9
10
11
12
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;
}

BackStackState#instantiate方法中,依据了在saveAllState时,构造BackStackState的规则来完整地恢复BackStackRecord的各个字段,包括mOps,具体可以参见BackStackState类。

至此,该方法的流程就走完了,期间Fragment除了实例化并恢复了基本状态,却并没有发生状态转移(moveToState),Fragment的mState字段依然是INITIALIZING状态,也就是说,对于整个大流程而言,还差一步状态的转移,Fragment的整体状态就可以恢复为应用上次离开(killed)时的样子了。而这一步就发生在Activity的onCreate方法里mFragments.restoreAllState的后面:

1
2
3
4
5
6
7
8
// Activity | FragmentActivity
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
mFragments.restoreAllState(...);
...// here
mFragments.dispatchCreate();
...
}

mFragments.dispatchCreate()方法就很熟悉了,具体可以参见Fragment的生命周期状态分析一文。这里仅列出在saveAllState时保存在各个Key下的状态通过dispatchCreate的触发,最终是在什么地方被恢复的,dispatchCreate()最终来到了moveState的5个参数的方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// FragmentManager#moveState
...
if (f.mState < newState) {
...
switch (f.mState) {
case Fragment.INITIALIZING:
...
if (f.mSavedFragmentState != null) {
f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(FragmentManagerImpl.VIEW_STATE_TAG);
f.mTarget = getFragment(f.mSavedFragmentState, FragmentManagerImpl.TARGET_STATE_TAG);
if (f.mTarget != null) {
f.mTargetRequestCode = f.mSavedFragmentState.getInt(FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
}
f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
...
}
...
if (!f.mRetaining) {
f.performCreate(f.mSavedFragmentState);
} else {
f.restoreChildFragmentState(f.mSavedFragmentState);
f.mState = Fragment.CREATED;
}
...
}
...

上面同时恢复的字段包括了mSavedViewState 、mTarget 、mTargetRequestCode 、mUserVisibleHint等,对于mSavedFragmentState,Fragment的生命周期方法中的这些方法也会用到:

1
2
3
4
5
6
f.performCreateView(f.getLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState);
f.onViewCreated(f.mView, f.mSavedFragmentState);
f.performActivityCreated(f.mSavedFragmentState);
if (f.mView != null) {
f.restoreViewState(f.mSavedFragmentState);
}

也就是说,我们可以选择在以上生命周期任何一个方法中来恢复我们先前保存的状态,并且这些状态都是同一个对象。

总结

分析Fragment的状态保存与恢复实则是分析FragmentManager的状态保存与恢复,对于Fragment的管理类,除了Fragment个体的状态保存恢复,Fragment之间在FragmentManager下的相互关系、结构栈等也需要维持,因此整个过程以FragmentManagerState为核心,经历了FragmentManagerState的“构造、持久化、还原”三大步骤,逐步完成了FragmentManager的状态保存与恢复。具体来说,就是在destroy前保存相关字段状态,交由系统管理,然后在re-created Activity时,先后创建全新FragmentManager及相关Fragment对象来关联上次保存的状态字段,实现状态的恢复。

本篇属于个人计划的分析Fragment系列。关于Fragment的字段详解、静态/动态加载流程、生命周期和状态的切换以及常见异常解决方案等的分析会陆续完成,欢迎关注。