Fragment源码阅读笔记

0 认知

Fragment官方的翻译名为:片段,表示 Activity中的行为或用户界面部分。

相比Activity
相比Activity,Fragment的创建、销毁只需要依附到宿主Activity中,不需要与ActivityManagerService跨进程交互,所有的生命周期在宿主Activity中完成,可以在多个FragmentActivity中被多次重用,所以它更加灵活。

相比View
相比View,它拥有更多的生命周期(onAttach、onCreate、onCreateView、onStart、onResume、onPause、onStop、onDestroyView、onDestroy),可以管理menu,持有Activity引用(View持有的context有可能为ContextThemeWrapper对象),更利于模块化。

1 构造

Fragment有两种方式创建并依附到宿主Activity。

fromLayout方式
在xml中配置fragment标签,例如

<FrameLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment
        android:tag="tag"
        android:name="com.asha.fragmentdemo.MyDialogFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</FrameLayout>

记得fragment必须带上1、android:name;2、android:tag或android:id二选一,否则会在创建过程中检查参数时报错。
当这个layout被inflate后,LayoutInfalter会回调Activity中的FragmentManager去处理这个tag,根据android:name实例化此tag对应的Fragment对象,通知它生成并返回view。随后由FragmentManager管理此Fragment的生命周期。

FragmentManager方式
在代码中实例化Fragment,被创建一个Bundle作为参数存储载体赋值给Fragment,随后通过FragmentManager开启个transaction、add Fragment、commit,随后某个时间点,FragmentMangager会处理此commit提交的aciton,完成Fragment的依附,示例代码如下:

blankFragment = new BlankFragment();
Bundle bundle = new Bundle();
bundle.putInt("data",-1);
blankFragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction().add(R.id.container, blankFragment, "BlankFragmentTag").commit();

通过setArguments(bundle)有利于保存与恢复,后面会有介绍。

2 状态

fragment的状态变化由FragmentManager管理,fragment状态主要可以分为以下6种:

//android.support.v4.app.Fragment
static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
static final int STOPPED = 3;          // Fully created, not started.
static final int STARTED = 4;          // Created and started, not resumed.
static final int RESUMED = 5;          // Created started and resumed.

2.1 fragment状态变化

fragment状态变化主要来自以下两个方面:

宿主FragmentActivty的生命周期变化
FragmentActivity生命周期的变化会调用FragmentController的对应回调,如当FragmentActivity调用onDestory后,成员变量FragmentController对象被调用了dispatchDestroy,代码如下

//android.support.v4.app.FragmentActivty
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

@Override
protected void onDestroy() {
    super.onDestroy();

    doReallyStop(false);

    mFragments.dispatchDestroy();
    mFragments.doLoaderDestroy();
}

对应FragmentController内传递了给了构造时聚合进来的mHost对象中的FragmentManager对象,代码如下

// android.support.v4.app.FragmentController
public void dispatchDestroy() {
    mHost.mFragmentManager.dispatchDestroy();
}

随后调用到FragmentManager中的moveToState方法,处理状态改变,代码如下

// android.support.v4.app.FragmentManager
public void dispatchDestroyView() {
    moveToState(Fragment.CREATED, false);
}

FragmentTransaction
与上一条直接在主线程中立即调用不同,FragmentTransaction添加一系列add、remove、replace操作op并链表形式存储,执行commit后,会在FragmentManager.enqueueAction,通过handler.post方法在主线程中下一个未知时间点执行此action,此action代码如下:

// android.support.v4.app.BackStackRecord extends FragmentTransaction

public void run() {
    ...
    Op op = mHead;
    while (op != null) {
        ...
        switch (op.cmd) {
            case OP_ADD:
                ...
                mManager.addFragment(f, false);
                break;
            case OP_REPLACE:
                ...
                mManager.removeFragment(old, transition, transitionStyle);
                ...
                mManager.addFragment(f, false);
                ...
                break;
            case OP_REMOVE:
                ...
                mManager.removeFragment(f, transition, transitionStyle);
                break;
            case OP_HIDE:
                ...
                break;
            case OP_SHOW:
                ...
                break;
            case OP_DETACH: 
                ...
                break;
            case OP_ATTACH:
                ...
                break;
            default:
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
        }
        op = op.next;
    }
    mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
    ...
}

在上述removeFragment、addFragment等操作都会进入对应的moveToState函数,最后mManager调用moveToState函数来同步目前管理的fragment状态迁移到mManager.mCurState状态。

2.2 moveToState

FragmentManager中moveToState函数有多个参数形式,moveToState方法所有形参定义如下:

// android.support.v4.app.FragmentManager

void moveToState(Fragment f)
void moveToState(int newState, boolean always)
void moveToState(int newState, int transit, int transitStyle, boolean always)
void moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive) 

举个例子,当FragmentActivity已经onResume后,即FragmentActivity已经显示在屏幕中,此时FragmentActivity中onResume已经调用了mFragmentManager的dispatchResume函数,即

// android.support.v4.app.FragmentManager

public void dispatchResume() {
    mStateSaved = false;
    moveToState(Fragment.RESUMED, false);
}

通过层层调用,进入到了上述第三个moveToState,在此代码中,FragmentManager实例的成员变量mCurState直接被赋值为Fragment.RESUMED状态,随后遍历实例内管理的mActive数组中的fragment对象,让他们进入到Fragment.RESUMED状态,代码如下:

// android.support.v4.app.FragmentManager

int mCurState = Fragment.INITIALIZING;
void moveToState(int newState, int transit, int transitStyle, boolean always) {
    ...
    mCurState = newState;
    if (mActive != null) {
        ...
        for (int i=0; i<mActive.size(); i++) {
            Fragment f = mActive.get(i);
            if (f != null) {
                moveToState(f, newState, transit, transitStyle, false);
                ...
            }
        }
        ...
    }
}

先忽略现有fragment的状态迁移,如果此时有新的fragment通过FragmentTransaction加入到mManager(为FragmentManager实例),上面分析过android.support.v4.app.BackStackRecord会在某个时间点执行action,此时新的frament被加入到mManager.mActive数组中,同时会调用mManager.moveToState同步到现有状态,即Fragment.RESUME/5,代码如下:

// android.support.v4.app.BackStackRecord extends FragmentTransaction

public void run() {
      ...
      // mManager为FragmentManager实例
      mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
      ...
}

所以执行到了FragmentManager的第四个moveToState,此时新的Fragment刚被创建成功,成员变量mState默认INITIALIZING/0,

// android.support.v4.app.Fragment
int mState = INITIALIZING;

FragmentManager的第四个moveToState函数带有Fragment参数,进入后先会使用f.mState(即fragment当前状态)与目标状态newState进行比较,f.mState即0 < newState即5,则fragment需要从状态0到状态5,分别需要经历INITIALIZING/0、CREATED/1、ACTIVITY_CREATED/2、STOPPED/3、STARTED/4,最后赋值为5,注意switch中没有break,需要一直按顺执行,不同的状态分支需要执行不同的函数通知fragment进入此状态,同时回调fragment中对应的生命周期函数。从代码框架如下:

// android.support.v4.app.FragmentManager

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive) {
    ...
    if (f.mState < newState) {
        ...
        switch (f.mState) {
            case Fragment.INITIALIZING:
                ...
                f.onAttach(mHost.getContext());
                ...
                if (!f.mRetaining) {
                    f.performCreate(f.mSavedFragmentState);
                }
                f.mRetaining = false;
                if (f.mFromLayout) {
            ...
                    f.onViewCreated(f.mView, f.mSavedFragmentState);
                }
            case Fragment.CREATED:
                if (newState > Fragment.CREATED) {
                    if (!f.mFromLayout) {
                        ...
                        f.onViewCreated(f.mView, f.mSavedFragmentState);
                    }
                    f.performActivityCreated(f.mSavedFragmentState);
                    ...
                }
            case Fragment.ACTIVITY_CREATED:
            case Fragment.STOPPED:
                if (newState > Fragment.STOPPED) {
                    f.performStart();
                }
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {
                    ...
                    f.performResume();
                    ...
                }
        }
    } else if (f.mState > newState) {
        switch (f.mState) {
            case Fragment.RESUMED:
                if (newState < Fragment.RESUMED) {
                    f.performPause();
                    ...
                }
            case Fragment.STARTED:
                if (newState < Fragment.STARTED) {
                    f.performStop();
                }
            case Fragment.STOPPED:
                if (newState < Fragment.STOPPED) {
                    f.performReallyStop();
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState < Fragment.ACTIVITY_CREATED) {
                    ...
                    f.performDestroyView();
                    ...
                }
            case Fragment.CREATED:
                if (newState < Fragment.CREATED) {
                    ...
                    if (!f.mRetaining) {
                        f.performDestroy();
                    }
                    f.onDetach();
                }
        }
    }
    f.mState = newState;
}   

同理从RESUMED状态被destroy,需要从5迁移到0,执行上述函数f.mState > newState部分的逻辑,一步一步回到INITIALIZING状态。

当然如果宿主Activity与fragement同时被销毁,fragement会接收到FragmentActivity对应的生命周期dispatch,从5到4,从4到3,从3到2,从2到1,从1到0即可完成状态迁移。

3 生命周期

fragment_lifecycle.png

先来一张官方文档的图,第二章已经介绍了状态迁移,对应的状态改变会调用对应的生命周期回调,调用时机已经非常清晰,说一下几个注意点:

onAttach后即持有activity引用
不要被onActivityCreated迷惑,fragment.onAttach时就可以使用activity了,因为在f.onAttach前就进行了一系列基础变量的赋值,代码如下:

// android.support.v4.app.FragmentManager

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
    ...
    if (f.mState < newState) {
        ...
        switch (f.mState) {
            case Fragment.INITIALIZING:
                ...
                f.mHost = mHost;
                f.mParentFragment = mParent;
                f.mFragmentManager = mParent != null
                        ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
                f.mCalled = false;
                f.onAttach(mHost.getContext());
                ...
        }
        ...
    }
    ...
}

onActivityCreated是一个什么状态?
FragmentActivity在调用onCreate开始的时候调用FragmentManager实例的dispatchCreate函数,在FragmentActivity在调用onCreate结束的时候会调用dispatchActivityCreated,即通知fragment的activity已经调用onCreate完毕。

为什么fromLayout的Fragment在INITIALIZING阶段就需要onViewCreated?
FragmentActivity在onCreate的时候调用了setContentView,此时需要通过R.layout.xxx方式或者直接LayoutInflater的方式创建view,LayoutInflater创建view时在createViewFromTag函数内会根据xml里定义的tag和attr去实例化对应的View,当LayoutInflater读取到fragment这个tag后,先让LayoutInflater内的context去处理onCreateView,看是否能返回对应的view,层层调用进入FragmentManager.onCreateView去实例化fragment、赋值、状态迁移,关键是在这个时候就需要返回fragment内对应的view,此时FragmentActivity在onCreate阶段,FragmentManager应该在Fragment.CREATED阶段,所以状态同步时newStateFragment.CREATED,在Fragment.CREATED分支无法调用onViewCreated,而fromLayout的Fragment在INITIALIZING阶段就需要创建view并返回了,所以在INITIALIZING就得调用了onViewCreated了。

如果fragment.setRetainInstance(true),在一定情况下生命周期函数调用就发生改变了
这个一定情况是指configChange的情况,下一章具体讲。

4 状态保存

4.1 需要保存哪些东西?

Fragment
如果需要重新构造一个除了内存地址不一样,属性与原来实例一模一样的Fragment,需要序列化以下四个对象或属性:

- FragmentState
FragmentState包含了重新构造这个Fragment所需的最基本的属性,包括完整类名、在mActive内的index,是否从layout生成的,id,tag,容器id,是否retainInstance,是否已经detached,构造时传入的参数,定义如下:

// android.support.v4.app.FragmentState

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;
}

- ViewState
Fragment内部管理的view的状态,我们知道view自身有一套状态保存的机制,通过根节点的view一层一层dispatch出去(dispatchSaveInstanceState、dispatchRestoreInstanceState)触发保存和恢复先前的状态。这种恢复属于view被新建实例后恢复原来的状态,比如EditText选中了一段文字,旋转屏幕重新创建view实例,会重新focus,重新选中刚刚所选的那段文字。

而由Fragment管理的view脱离了原来的dispatch流程,是由Fragment自主管理触发saveViewState和restoreViewState,脱离dispatch的方法在sdk11之前wrap一个NoSaveStateFrameLayout,11及之后直接设置属性即可,代码如下:

// android.support.v4.app.FragmentManager
// moveToState

f.mView = f.performCreateView(f.getLayoutInflater(
        f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
    f.mInnerView = f.mView;
    if (Build.VERSION.SDK_INT >= 11) {
        ViewCompat.setSaveFromParentEnabled(f.mView, false);
    } else {
        f.mView = NoSaveStateFrameLayout.wrap(f.mView);
    }
    ...
}

- mUserVisibleHint
用这个标记可以控制Fragment是否延迟执行mLoaderManager内的任务,即如果mUserVisibleHint为false,这个Fragment在需要迁移到大于STOPPED的状态时先忽略,当所有其他mUserVisibleHint为true的Fragment内的runningLoader执行完成,再迁移到FragmentManager现有状态。

那为什么是停止在STOPPED状态?
因为这个状态下一个状态就会触发Fragment.onStart,onStart会调用这个Fragment内的mLoaderManager启动内部的loader去做加载操作,如果延迟加载这部分,可以让其他更重要的loader做完操作后再进行,提升体验。

- 可自定义的onSaveInstanceState
这个与Activity一致,不再赘述。

FragmentManager
FragmentManager与Fragment一样,都有一个序列化、反序列化基础属性的State类:FragmentManagerState。FragmentManagerState保存三个可序列化对象数组:

// android.support.v4.app.FragmentManagerState
final class FragmentManagerState implements Parcelable {
    FragmentState[] mActive;
    int[] mAdded;
    BackStackState[] mBackStack;
}

对应保存FragmentManager内的三个数组属性,定义如下:

// android.support.v4.app.FragmentManager

ArrayList<BackStackRecord> mBackStack;
ArrayList<Fragment> mActive;
ArrayList<Fragment> mAdded;

mAdded内保存的是所有add到FragmentManager内的Fragemnt,mActive中包含了所有mAdded对象外,还保存了与backStack相关的所有Fragment。所以说mAdded是mActive的子集,对应序列化对象时,mAdded只需要记住这个Fragment对象在mActive中的索引值,就可以找回原来Fragment对应的新Fragment。mBackStack保存了所有addToBackStack的FragementTransaction,可以记录某次commit操作所有Fragment的变化,便于按下back键后回滚到上一步。

4.2 Fragment保存机制

fragment有两种保存机制,一种是fragment.onSaveInstanceState方式,另一种是fragment.setRetainInstance(true)方式,我们来看看以下几个经常出现的场景:

宿主FragmentActivity从后台直接恢复
由于FragmentActivity只是在onStop状态,FragmentActivity内的FragmentManager实例状态为STOPPED状态,FragmentManager实例和其内部管理的Fragment实例都还健在,只是需要从STOPPED状态迁移到RESUMED即可。

FragmentActivity的recreate
recreate有两种情况会触发,一种是直接调用Activity.recreate(),另一种是RELAUNCH_ACTIVITY。两种方式走到AMS层后都是走相同的流程。

RELAUNCH_ACTIVITY会在旋转屏幕等onConfigurationChanged的情况未被Activity处理后发生。例如发生了ConfigurationChanged,而Manifest.xml中此Activity的android:configChanges没有配置此Configuration,即Activity不处理此Configuration,AMS就会RELAUNCH此Activity。

发生recreate后,AMS会销毁现有的Activity实例,重新启动一个新的Activity实例。

如果Fragment设置了fragment.setRetainInstance(true)
AMS在销毁旧Activity实例时会调用ActivityThread.performDestoryActivity -> Activity.retainNonConfigurationInstances -> FragmentActivity.onRetainNonConfigurationInstances -> FragmentController.retainNonConfig -> mFragmentManager.retainNonConfig,在FragmentManager中返回了mActive数组拷贝,代码如下:

// android.support.v4.app.FragmentManager

ArrayList<Fragment> retainNonConfig() {
    ArrayList<Fragment> fragments = null;
    if (mActive != null) {
        for (int i=0; i<mActive.size(); i++) {
            Fragment f = mActive.get(i);
            if (f != null && f.mRetainInstance) {
                if (fragments == null) {
                    fragments = new ArrayList<Fragment>();
                }
                fragments.add(f);
                f.mRetaining = true;
                ...
            }
        }
    }
    return fragments;
}

我们来看看Activity中保存一部分实例,代码如下:

// android.app.Activity

NonConfigurationInstances retainNonConfigurationInstances() {
    Object activity = onRetainNonConfigurationInstance();
    HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
    List<Fragment> fragments = mFragments.retainNonConfig();
    ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
    ...

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.activity = activity;
    nci.children = children;
    nci.fragments = fragments;
    nci.loaders = loaders;
    if (mVoiceInteractor != null) {
        mVoiceInteractor.retainInstance();
        nci.voiceInteractor = mVoiceInteractor;
    }
    return nci;
}

nci所保存的对象可以是任意对象,不需要做序列化和反序列化操作,恢复时是原有对象实例。nci对象返回给ActivityThread,保存在此进程的ActivityThread对象的mActivities键值对的此Activity Binder对应的ActivityClientRecord中。值得注意的是,nci.activity是在Activity中调用onRetainNonConfigurationInstance返回的对象,不是此Activiyt实例。

所以原Activity被recreate后,会生成新的Activity,新的FragmentManager,但是是旧的Fragment对象,所以它的生命周期会有所不同,它不会有onCreate和onDestroy,不会被FragmentManager从mActive中删除,因为在Activity onDestroy时需要被retainNonConfig保存下来。

当新Activity onCreate后会调用FragmentManager的restoreAllState,在此把之前保存在nci对象里的mActive重新取出来,一个一个赋值给新建的mActive对象,完成nonConfig对象的恢复。

如果Fragment未设置fragment.setRetainInstance(true),默认为false
则走原有流程。

宿主FragmentActivity从后台恢复时由于内存不足已经被kill
众所周知Activity的onSaveInstanceState在onPause之后在onStop之前,所以当Activity在被放到后台即onStop前会调用onSaveInstanceState,在此函数中调用了FragmentManager.saveAllState,主要存储FragmentManager的三个数mActive、mAdded、mBackStack的内容到FragmentManagerState中,mActive中保存了上述的FragmentState。当Activity被重新创建调用onCreate时会得到刚刚保存的savedInstanceState,再通过这个savedInstanceState获得刚保存的FragmentManagerState,去创建一个新的FragmentManager对象,去重新生成Fragment。此时恢复的FragmentActivity、FragmentManager、Fragment都是新的实例。

此时不管fragment是否setRetainInstance(true),Fragment实例都会重新被创建,原因一:retainNonConfig是在Activity在onDestroy被保存的;原因二:只有被relaunch的activity在destroy时才会在ActivityThread代码中被调用retainNonConfig去通知Activity返回需要保存实例,其他的destroy不会。

4.3 补充

3.0后处理旋转屏幕ConfigurationChanged
3.0后需要配置screenSize来处理旋转屏幕ConfigurationChanged

<activity android:name=".MainActivity" android:configChanges="orientation|screenSize">

如果配置了这个属性,旋转屏幕,Activity只会回调onConfigurationChanged,不会调用其他任何生命周期函数,当然也不会被重新生成实例,FragmentManager实例、Fragment实例都是不会发生变化的。

5 小细节

remove fragment
如果Fragment通过layout.xml方式加入到Activity中,被FragmentManager进行remove或者replace操作后,Fragment实例在FragmentManager中被删去,而Fragment内对应的view没有被赋值mContainerView,所以内部的view没有被移除,导致界面一直存在,此时这个Fragment已经被detach,如果再对它调用getActivity将返回null。

add fragment
如果Activity没有处理screenSize的onConfigurationChanged,那么此Activity将被recreate。在重新调用Activity onCreate时,FragmentManager的mActive和mAdd内的Fragment被重新创建,如果此时在Activity的onCreate重新add一个Fragment,那么就会出现两个Fragment的情况。
如何解决?在添加前先检查下FragmentManager内是否存在此Fragment,不存在再添加即可,代码如下:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(v);
    BlankFragment blankFragment = (BlankFragment) getSupportFragmentManager().findFragmentByTag("BlankFragmentTag");
    if ( blankFragment == null ){
        blankFragment = new BlankFragment();
        blankFragment.setUserVisibleHint(false);
        Bundle bundle = new Bundle();
        bundle.putInt("data",-1);
        blankFragment.setArguments(bundle);
        getSupportFragmentManager().beginTransaction().add(R.id.container, blankFragment, "BlankFragmentTag").commit();
    }
}

hide fragment
因为hide状态没有被保存下来,在recreate或者内存不足重启Activity的情况下,原来被hide的Fragment将被重新show,所以得注意这个问题。

replace fragment
目前的版本存在一个bug,一个ViewGroup加了一个以上Fragment后,FragmentManager去replace此ViewGroup内的Fragement无法正确replace,原因是在ArrayList for循环里做了ArrayList remove操作,目前还没修复。

addBackStack情况下,remove Fragment后Fragment还继续存在mActive中
由于remove后有可能popBackStack(),所以mAdd内被删除后,mActive内还保存着此Fragment引用,此时findFragmentById或者findFragmentByTag都可以找到这个Fragment。同理,setRetainInstance(true)的Fragment被remove后也存在在mActive中。

DialogFragment中的onCreateView
同一个activity中的fragment和DialogFragment用onCreateView中所带的参数inflater去inflate view,view.getContext()返回值是不同的。。前者是此activiy;后者返回的是ContenxThemeWrapper,内部wrap了activity。原因是dialog要创建新的context存放对应的theme去inflate view。

6 reference

本文所涉及源码版本为

compile 'com.android.support:support-v4:23.1.0'

推荐阅读更多精彩内容