Fragment源码解析三——状态保存与恢复

这一篇单独来说一说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.java
protected 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.java
Parcelable saveAllState() {
        .....
        FragmentManagerState fms = new FragmentManagerState();
        fms.mActive = active;
        fms.mAdded = added;
        fms.mBackStack = backStack;
        return fms;
    }
//FragmentManagerState.java
final 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中保存了以下这些状态:

信息(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) 供用户手动保存的状态

mAdded成员

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

// FragmentManagerImpl.java
Parcelable 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.java
Parcelable 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
@Override
public 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.java
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) {
        ...
    }
    ...
}

(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.java
public 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状态进行转移。


女票良心美代,只做正品的搬运工。如果你觉得这篇文章帮助到了你一点点,扫码支持一下吧^ ^


二维码
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容