美文网首页fragment源码解析android开发
通过源码解析 Fragment 启动过程

通过源码解析 Fragment 启动过程

作者: __huazhou | 来源:发表于2016-04-20 15:29 被阅读5795次

    0x00

    作为一名 Android 开发者,大家绝对都接触过 Fragment 开发,而且绝大多数人例如我一直都很难记住下图 Fragment 复杂的生命周期,更别说要将其与 Activity 的生命周期关联起来。


    死背是无法解决问题的,若我们能从源码的角度对 Fragment 的启动过程进行分析,就能达到事半功倍的效果。那么我们选择上图中的第一部分入手吧。

    下面我将会分析 Fragment 这一部分的启动过程,剩下部分的逻辑没有多大差异,所用的 Android SDK 版本为 23

    0x01

    一般情况下我们 MainActivity 需要显示一个 ContentFragment,代码可以这么写。

    //MainActivity
    public class MainActivity extends Activity{
      ContentFragment mContentFragment;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        mContentFragment = ContentFragment.newInstance(null);
        getFragmentManager()
            .beginTransaction()
            .replace(R.id.container,mContentFragment)
            .commit();
      }
    }
    

    同时为了方便起见,我大概介绍下我们即将遇到的几个类。第一次看的时候可能印象不深,但没关系,后面还会反复提起。


    上面 UML 类图并不是完整的,但已经足够了。
    1. ActivityThread : 大名鼎鼎的 Android 入口类,我们的分析也将会从它开始
    1. Instrumentation : ActivityThread 的得力助手,帮助 ActivityThread 触发 Activity 的生命周期
    2. MainActivity : 就是上文提到例子中的 MainActivity 类,继承自 Activity
    3. HostCallbacks : Activity 的内部类,继承自 FragmentHostCallback
    4. FragmentHostCallback : 持有 Handler、FragmentManagerImpl 等等对象的引用,别的对象可以通过持有它的引用间接控制 FragmentManagerImpl 等等对象
    5. FragmentController : Activity 通过控制它间接向 FragmentManagerImpl 发出命令
    6. FragmentManagerImpl : 顾名思义,它继承自 FragmentManager,用来对 Fragment 进行管理,在 FragmentHostCallback 中被初始化
    7. BackStackRecord : 继承自 FragmentTransation 并实现了 Runnable,每次调用 FragmentManager 对象的 beginTransaction() 方法都会产生一个 BackStackRecord 对象,可以将其理解为对 Fragment 的一系列操作(即事务)
    8. Op : 每次对 Fragment 的操作都会产生一个 Op 对象,其表示双向链表的一个结点

    哈哈,没印象无所谓...

    0x02

    那从哪里开始呢,我们注意到 Activity 类中有个 FragmentController 实例,那它是怎么初始化的?我们直接在 Activity 中搜索 FragmentController 关键字,即可找到如下代码:

    //Activity
    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
    

    原来在声明成员变量的同时初始化的呀,那也就是说在 Activity 被实例化时该成员变量也会被初始化,那么 Activity 又是在哪里被实例化的呢?作为一位看过 Activity 启动过程源码解析的男人,一下子就能想到 ActivityThread 的 performLaunchActivity 方法(当然,没看过 Activity 启动过程源码的朋友也没事,这篇文章分析的流程比那个简单多了,而且我们这里并不关注 Activity 的启动过程,看了这个或许对看那个有一定帮助),那我们去 ActivityThread 类的 performLaunchActivity 方法找下吧

    //ActivityThread
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        /*这个方法最终会通过反射调用 Activity 的无参构造方法*/
        //...
    } catch (Exception e) {
        //...
    }
    //上面代码省略了一些非本次该关注的逻辑,下同
    

    我们从 Activity 的实例化这里开始讲起吧

    0x03

    上文说到,Activity 会在 ActivityThread 的方法 performLaunchActivity 中通过反射实例化,相当于调用了它的无参构造方法,接着会触发 Activity 的成员变量 mFragments 的初始化,即

    //Activity
    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
    

    直接实例化了一个 HostCallbacks 对象并将其作为参数传入 FragmentController 类的 createController 静态方法中。

    I

    好的,我们先看看 HostCallbacks 初始化做了啥。

    //HostCallbacks(因为 HostCallbacks 是 Activity 的内部类,所以是在 Activity.java 文件里)
    public HostCallbacks() {
        super(Activity.this );
    }
    

    嗯,因为 HostCallbacks 是 Activity 的内部类,所以直接用 Activity.this 作为参数调用它父类的构造方法,通过上文我们知道它的父类是 FragmentHostCallback

    //FragmentHostCallback
    FragmentHostCallback(Activity activity) {
        this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);
    }
    
    FragmentHostCallback(Activity activity, Context context, Handler handler,
            int windowAnimations) {
        mActivity = activity;
        mContext = context;
        mHandler = handler;
        mWindowAnimations = windowAnimations;
    }
    

    我们可以了解到它最终调用了 FragmentHostCallback 四个参数的重载构造方法,并会将 Activity 、Handler 等等对象保存到它的成员变量中。上文说到 FragmentManagerImpl 会在 FragmentHostCallback 中被初始化,看来它不是在构造方法中被初始化,那我们到成员变量的声明处看看吧。

    //FragmentHostCallback
    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
    

    又是在声明的同时初始化。FragmentManagerImpl 继承自抽象类 FragmentManager,FragmentManagerImpl 采用的是默认的构造方法,所以这部分的分析就到此为止了。对了,FragmentManagerImpl 和 FragmentManager 两个类都是在 FragmentManager.java 文件中的,而且不是内外部类关系,FragmentManagerImpl 类的声明如下

    //FragmentManager
    final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2{
      //...
    }
    

    作用域是包级的,包名是 android.app

    II

    上一节后我们已经有了一个 HostCallback 对象,并将其作为参数传给 FragmentController 的 createController 静态方法,我们现在进去看看吧。

    //FragmentController
    public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
        return new FragmentController(callbacks);
    }
    
    private FragmentController(FragmentHostCallback<?> callbacks) {
        mHost = callbacks;
    }
    

    原来 FragmentController 隐藏了自己的构造方法,createController 就如同工厂方法,最后令 FragmentController 对象持有了 HostCallbacks 对象的引用。这样 FragmentController 就能通过 HostCallbacks 对象的引用来间接调用 FragmentManagerImpl 了啦,因为你看上面 FragmentHostCallback 类中 mFragmentManager 的声明也是默认包级的。

    阶段小结

    在 Activity 实例化的过程中,初始化了它的成员变量 FragmentController,FragmentController 持有了 HostCallbacks 对象的引用,HostCallbacks 是 Activity 的内部类,HostCallbacks 用 Activity 对象作为参数调用自己父类 FragmentHostCallback 的构造方法,接着 FragmentHostCallback 会持有了 Activity、Handler 等等对象的引用,并在声明 mFragmentManager 成员变量的同时实例化 FragmentManagerImpl 对象。

    0x04

    在 ActivityThread 类的 performLounchActivity 方法里 Activity 被实例化后不久会被调用重量级的 attach 方法,我们试试到这方法下看看是否有我们需要的信息。

    //Activity
    mFragments.attachHost(null /*parent*/);
    

    我们知道 mFragments 就是 FragmentController 对象的引用,看来这里确实有些初始化操作,那为什么是传入 null 呢?我们看下方法吧。

    //FragmentManagerImpl
    public void attachHost(Fragment parent) {
        mHost.mFragmentManager.attachController(
                mHost, mHost /*container*/, parent);
    }
    

    因为 Fragment 下也能有自己的子 Fragment,而现在我们的 Fragment 是在 Activity 下的,所以这里当然就直接传入 null 啦。我们再注意到本方法调用了 mHost 的 mFragmentManager 成员变量的 attachController 方法,我们只需关心方法的第一个参数。

    //FragmentManagerImpl
    public void attachController(FragmentHostCallback<?> host, FragmentContainer container,
            Fragment parent) {
        if (mHost != null) throw new IllegalStateException("Already attached");
        mHost = host;
        mContainer = container;
        mParent = parent;
    }
    

    嗯,现在 FragmentManagerImpl 对象也持有了 HostCallbacks 对象的引用。

    阶段小结

    这一部分我们关注的仅仅是 FragmentManagerImpl 对象持有了 HostCallbacks 对象的引用。

    0x05

    我们知道 Fragment 的生命周期与 Acitivty 的有对应关系,那么我们也就可以猜到 Activity 的生命周期方法被触发时,Activity 会同时触发 Fragment 对应的生命周期方法,而且是通过自己持有的 FragmentController 对象来触发的,我们来看看是不是这样子吧。Activity 生命周期第一个被调用的方法是 onCreate 方法,它是在哪里被触发的呢?现在就是 ActivityThread 的得力助手 Instrumentation 出场的时候了!
    还是在 ActivityThread 的 performLounchActivity 方法中,我们发现如下代码

    //ActivityThread
    mInstrumentation.callActivityOnCreate(activity, r.state);
    

    跳去 Instrumentation 类看看

    //Instrumentation
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        //...
        activity.performCreate(icicle);
        //...
    }
    

    调用了 performCreate 方法,注意这里的 activity 的运行时类型是 MainActivity,因为我们的 MainActivity 继承了 Activity,但 performCreate 方法是 final 的,MainActivity 无法重写,所以调用了其父类 Activity 的 performCreate 方法,我们去看下

    //Activity
    final void performCreate(Bundle icicle) {
        onCreate(icicle);
        //...
        performCreateCommon();
    }
    

    先调用 onCreate方法再调用 performCreateCommon方法,同时我们注意到 onCreate 方法最先被调用而 performCreateCommon 最后被调用,两个方法都会分别触发 Fragment 的生命周期方法,这样安排是有目的的。下面我们分别分析。

    PS : 下面是一连串的调用,先休息下,揉揉眼睛,喝喝水吧。

    I

    因为 MainActivity 重写了 Activity 的 onCreate 方法,我们先再看下我们重写的 onCreate 方法。

    //MainActivity
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mContentFragment = ContentFragment.newInstance(null);
    
    getFragmentManager()
        .beginTransaction()
        .replace(R.id.container,mContentFragment)
        .commit();
    }
    

    这个方法做了挺多操作的,我们还是一个一个来哈。

    I-I

    首先调用了父类 Activity 的 onCreate 方法,我们去看看做了啥。

    //Activity
    @MainThread
    @CallSuper
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        //...
        mFragments.dispatchCreate();
        //...
    }
    

    Activity 好懒哟,又让人家 FragmentController 干活,可怜的 FragmentController

    //FragmentController
    public void dispatchCreate() {
        mHost.mFragmentManager.dispatchCreate();
    }
    

    FragmentController 深得 Activity 精髓,把活推给了 FragmentManagerImpl,原来 FragmentManagerImpl 才是苦力。

    //FragmentManagerImpl
    public void dispatchCreate() {
        //...
        moveToState(Fragment.CREATED, false);
    }
    
    void moveToState(int newState, boolean always) {
        moveToState(newState, 0, 0, always);
    }
    
    void moveToState(int newState, int transit, int transitStyle, boolean always) {
        if (mHost == null && newState != Fragment.INITIALIZING) {
            throw new IllegalStateException("No activity");
        }
    
        if (!always && mCurState == newState) {
            return;
        }
    
        mCurState = newState;
        if (mActive != null) {
            //...
        }
    }
    

    上面有三个方法被顺序调用,到了最后一个 moveToState 方法时我们只需关注它的参数 newState = Fragment.CREATEDalways = false,而默认情况下成员变量 mCurState = Fragment.INITIALIZINGmActive = null,所以该方法执行后只有一个值得我们关注的细节 mCurState = Fragment.CREATED

    I-II

    MainActivity 的父类的 onCreate 方法执行完后我们该关注 getFragmentManager 方法了,到 Activity 中看看这个方法吧。

    //Activity
    public FragmentManager getFragmentManager() {
        return mFragments.getFragmentManager();
    }
    
    //FragmentController
    public FragmentManager getFragmentManager() {
        return mHost.getFragmentManagerImpl();
    }
    

    只是直接返回 HostCallbacks 对象持有的 FragmentManagerImpl 对象而已。

    I-III

    接下来就到了 FragmentManagerImpl 的 beginTransaction 方法了。

    //FragmentManagerImpl
    public FragmentTransaction beginTransaction() {
        return new BackStackRecord(this);
    }
    

    如我们所知,BackStackRecord 对象代表一系列对 Fragment 的操作,即事务,这里调用了 BackStackRecord 的构造方法并将 FragmentManagerImpl 自己作为参数传入。咱去看看构造方法做了啥。

    //BackStackRecord
    public BackStackRecord(FragmentManagerImpl manager) {
        mManager = manager;
    }
    

    这样 BackStackRecord 也持有了 FragmentManagerImpl 对象的引用。

    I-Ⅳ

    然后调用 BackStackRecord 的 replace 方法,即

    //BackStackRecord
    public FragmentTransaction replace(int containerViewId, Fragment fragment) {
        return replace(containerViewId, fragment, null);
    }
    
    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
        if (containerViewId == 0) {
            throw new IllegalArgumentException("Must use non-zero containerViewId");
        }
    
        doAddOp(containerViewId, fragment, tag, OP_REPLACE);
        return this;
    }
    

    先调用 replace 两个参数的重载方法,在该方法里调用了 replace 三个参数的重载方法,同时参数 tag = null,在这个方法里首先判断传进的用来装载 Fragment 的容器的 ID 是否有效,接着调用 doAddOpp 方法,它第三个参数传入了的是 OP_REPLACE 常量。这里注意它最后返回了 this,也就是我们的链式调用的下一个方法还是 BackStackRecord 对象的,结合下面我们脑海就能很形象地产生 Op 双向链表的图像。

    //BackStackRecord
    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        fragment.mFragmentManager = mManager;
    
        if (tag != null) {
            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
                throw new IllegalStateException("Can't change tag of fragment "
                        + fragment + ": was " + fragment.mTag
                        + " now " + tag);
            }
            fragment.mTag = tag;
        }
    
        if (containerViewId != 0) {
            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
                throw new IllegalStateException("Can't change container ID of fragment "
                        + fragment + ": was " + fragment.mFragmentId
                        + " now " + containerViewId);
            }
            fragment.mContainerId = fragment.mFragmentId = containerViewId;
        }
    
        Op op = new Op();
        op.cmd = opcmd;
        op.fragment = fragment;
        addOp(op);
    }
    

    在本方法首先让参数 fragment 持有 FragmentManagerImpl 的引用,接着就是判断参数 tagcontainerViewId 是否有效并赋值给 fragment 的成员变量。最后 Op 对象终于出场了(此处应有掌声)。实例化了一个 OP 对象后改变了它成员变量 cmd 的值,这里是 OP_REPLACE,接着让 Op 也持有 Fragment 对象的引用,最后就是将 Op 对象作为参数调用 addOp 方法。

    //BackStackRecord
    void addOp(Op op) {
        if (mHead == null) {
            mHead = mTail = op;
        } else {
            op.prev = mTail;
            mTail.next = op;
            mTail = op;
        }
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
        mNumOp++;
    }
    

    前面我们说过 Op 表示的是双向链表的一个结点,这里得到了证实。这个方法只是双向链表的创建及添加结点逻辑。

    I-V

    可以提交事务了,即调用了 BackStackRecord 对象的 commit 方法。

    //BackStackRecord
    public int commit() {
        return commitInternal(false);
    }
    
    int commitInternal(boolean allowStateLoss) {
        if (mCommitted) {
            throw new IllegalStateException("commit already called");
        }
        //...
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }
    

    最终调用了 commitInternal 方法,首先通过 mCommitted 判断是否已经提交过,因为默认为 false,所以这里不会抛出错误,接着最重要的逻辑就是调用了 mManager.enqueueAction(this, allowStateLoss),mManager 就是FragmentManagerImpl 对象,方法 enqueueAction 的第一个参数是 Runnable 类,但这里的 this 不是 BackStackRecord 对象么?

    //BackStackRecord
    final class BackStackRecord extends FragmentTransaction implements
            FragmentManager.BackStackEntry, Runnable{
            //...
    }
    

    看到了吧,BackStackRecord 实现了 Runnable,看来会有大事发生...

    //FragmentManagerImpl
    public void enqueueAction(Runnable action, boolean allowStateLoss) {
        //...
        synchronized (this) {
            if (mDestroyed || mHost == null) {
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {
                mPendingActions = new ArrayList<Runnable>();
            }
            mPendingActions.add(action);
            if (mPendingActions.size() == 1) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
            }
        }
    }
    

    这里将 Runnable 装入了 mPendingActions 中,因为此时 mPendingActions.size() == 1true,所以最终会将 mExecCommit 成员变量压入 mHost 持有的来自 Activity 的 Handler 对象里。那么我们现在可以推断出 mExecCommit 也是 Runnable 类型,同时它会在主线程被调用。我们看看 mExecCommmit 是何方神圣。

    //FragmentManagerImpl
    Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions();
        }
    };
    

    原来它的作用就是为了切换到主线程然后执行它外部类即 FragmentManagerImpl 的 execPendingActions 方法而已。现在我假设这个 Runnable 对象会被马上被调用,实际可能需要排队...好,到 execPendingAcions 方法了。

    //FragmentManagerImpl
    public boolean execPendingActions() {
        //...
        while (true) {
            int numActions;
            
            synchronized (this) {
                if (mPendingActions == null || mPendingActions.size() == 0) {
                    break;
                }
                
                numActions = mPendingActions.size();
                if (mTmpActions == null || mTmpActions.length < numActions) {
                    mTmpActions = new Runnable[numActions];
                }
                mPendingActions.toArray(mTmpActions);
                mPendingActions.clear();
                mHost.getHandler().removeCallbacks(mExecCommit);
            }
            
            //...
            for (int i=0; i<numActions; i++) {
                mTmpActions[i].run();
                mTmpActions[i] = null;
            }
            //...
        }
        //...
    }
    

    本方法我们只需关注首先将 mPendingActions 里的 Runnable 对象转移到 mTmpActions 数组里,并依次调用 mTmpActions 数组保存的 Runnable 的 run 方法。我们知道这里指的就是 BackStackRecord 中的 run 方法。去看看吧

    //BackStackRecord
    public void run() {
        //...
        Op op = mHead;
        while (op != null) {
            switch (op.cmd) {
                //...
                case OP_REPLACE: {
                    Fragment f = op.fragment;
                    int containerId = f.mContainerId;
                    //...
                    if (f != null) {
                        f.mNextAnim = op.enterAnim;
                        mManager.addFragment(f, false);
                    }
                }
                break;
                //...
            }
            op = op.next;
        }
    
        mManager.moveToState(mManager.mCurState, mTransition,mTransitionStyle, true);
        //...
    }
    

    这个方法其实很长的,但我们还是只要关注我们当前需要关注的逻辑就行啦。可以看到其实就是从头遍历 Op 双向链表,并通过判断 Op 对象里的 cmd 成员变量的值进行不同操作而已(其实这就是命令模式,可以看我的另一篇介绍设计模式的文章),这里我们只关注 OP_REPLACE。首先取出 Op 中保存的 Fragment 对象作为参数调用 FragmentManagerImpl 对象的 addFragment 方法,最后又通过 FragmentManagerImpl 对象的 moveToState 方法改变状态,还是一步一步来。

    PS : 咳咳,下面我还有几句话要说。哈哈,我画了张思维导图,或许对照着看能更好地理解。

    先看看 FragmentManagerImpl 的 addFragment 方法吧,注意这里第一个参数传入了 Fragment 对象,第二个参数传入 false

    //FragmentManagerImpl
    public void addFragment(Fragment fragment, boolean moveToStateNow) {
        if (mAdded == null) {
            mAdded = new ArrayList<Fragment>();
        }
        //...
        makeActive(fragment);
        if (!fragment.mDetached) {
            if (mAdded.contains(fragment)) {
                throw new IllegalStateException("Fragment already added: " + fragment);
            }
            mAdded.add(fragment);
            fragment.mAdded = true;
            fragment.mRemoving = false;
            //...
            if (moveToStateNow) {
                moveToState(fragment);
            }
        }
    }
    

    这里做了几件事:

    1. 通过 makeActive 方法将 Fragment 对象添加进 mActive 列表中。
    2. 将 Fragment 添加进 mAdded 列表中。
    3. 设置 Fragment 的一些属性。

    然后到 FragmentManagerImpl 的 moveToState 方法了。我们只需关心它的第一个参数,即 mManager.mCurState,我们知道这里的 mCurState 此时已经被赋值成了 Fragment.CREATED

    //FragmentManagerImpl
    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);
                    //...
                }
            }
            //...
        }
    }
    

    这里做了两件事

    1. 虽然 mCurState 已经是 Fragment.CREATED 了,还是被再次赋了一次 Fragment.CREATED 值。(当然不是多余,这次只不过是特殊而已啦)
    2. 和上次不同,这次 mActive 是非空的,那就遍历 mActive 取出 Fragment 作为参数并调用另一个 moveToState 方法。下面我们见识下这个方法吧(<- 大 BOSS)。方法很长,还是只关注我们需要那部分。
    //FragmentManagerImpl
    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());
                    //...
                    if (f.mParentFragment == null) {
                        mHost.onAttachFragment(f);
                    }
    
                    if (!f.mRetaining) {
                        f.performCreate(f.mSavedFragmentState);
                    }
                    f.mRetaining = false;
                    if (f.mFromLayout) {
                        //...
                    }
                //...
        }
        
        f.mState = newState;
    }
    

    首先注意这里 f.mState = Fragment.INITIALIZING(默认)newState = Fragment.CREATEDmParent = nullf.mRetaining = false(默认)f.mFromLayout = false(默认)。那么这里干了几件事。

    1. Fragment 获得了 HostCallbacks 对象的引用。
    2. Fragment 再次获得 FragmentManagerImpl 的引用。
    3. 调用了 Fragment 生命周期的第一个方法 onAttach!!
    4. 因为 f.mParentFragment == nulltrue,所以会调用 mHostonAttachFragment,进去看看
    //HostCallback
    @Override
    public void onAttachFragment(Fragment fragment) {
        Activity.this.onAttachFragment(fragment);
    }
    

    也就说 Activity 可以通过这个方法知道被依附的 Fragment 的实例。

    1. 因为 !f.mRetainingtrue,所以会接着调用 Fragment 的 performCreate 方法
    //Fragment
    void performCreate(Bundle savedInstanceState) {
        //...
        onCreate(savedInstanceState);
        //...
    }
    

    这里终于调用了 Fragment 的生命周期 onCreate 方法!!

    1. 方法最后改变了 Fragment 的状态,即 f.mState = newState = Fragment.CREATED
    2. 有个容易忽略的细节就是,在每个 case 块的最后是没有 break 关键字的(可不是我忽略掉的喔,你看之前那 switch 块就有 break 关键字),也就是说只要符合条件会继续到下一个 case 块执行。

    I-小结

    在 Activity 的 performCreate 方法中调用了 onCreate 方法后,做了如上操作。主要做了两件最关注的事:

    1. 改变了 FragmentManagerImpl 的 mCurStateFragment.CREATED
    2. 先切换到主线程后先后调用了 Fragment 的生命周期方法 onAttachonCreate 并将 Fragment 的状态即 mState 改为了 Fragment.CREATED

    II

    那么就到了 Activity 的 performCreate 方法中最后调用的 performCreateCommon 方法了。

    //Activity
    final void performCreateCommon() {
        //...
        mFragments.dispatchActivityCreated();
        //...
    }
    

    和上面一样,相同的逻辑,那么我们可以猜到下面其实也大同小异,那就全盘托出吧。

    //ActivityController
    public void dispatchActivityCreated() {
        mHost.mFragmentManager.dispatchActivityCreated();
        /*一样,调用了 FragmentManagerImpl 的 dispatchActivityCreated 方法*/
    }
    
    //FragmentManagerImpl
    public void dispatchActivityCreated() {
        //...
        moveToState(Fragment.ACTIVITY_CREATED, false);
        /*现在传入的状态常量是 Fragment.ACTIVITY_CREATED*/
    }
    
    //FragmentManagerImpl
    void moveToState(int newState, boolean always) {
        moveToState(newState, 0, 0, always);
    }
    
    //FragmentManagerImpl
    void moveToState(int newState, int transit, int transitStyle, boolean always) {
        //...
        /*mCurState 这里又被改成了 Fragment.ACTIVITY_CREATED*/
        mCurState = newState;
        if (mActive != null) {
            //...
            for (int i=0; i<mActive.size(); i++) {
                /*一样,遍历 mActive,调用 moveToState,分别将 Fragment 作为参数传入 */
                Fragment f = mActive.get(i);
                if (f != null) {
                    moveToState(f, newState, transit, transitStyle, false);
                    //...
                }
            }
            //...
        }
    }
    
    //FragmentManagerImpl
    //方法很长,不怕,我们在代码外分析
    void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
        //...
        if (f.mState < newState) {
            //...
            switch (f.mState) {
                //...
                case Fragment.CREATED:
                    if (newState > Fragment.CREATED) {
                        //...
                        if (!f.mFromLayout) {
                            ViewGroup container = null;
                            if (f.mContainerId != 0) {      
                                container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);
                                if (container == null && !f.mRestored) {
                                    //...
                                }
                            }
                            f.mContainer = container;
                            f.mView = f.performCreateView(f.getLayoutInflater(
                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
                            if (f.mView != null) {
                                //...
                                if (container != null) {
                                    Animator anim = loadAnimator(f, transit, true,
                                            transitionStyle);
                                    if (anim != null) {
                                        anim.setTarget(f.mView);
                                        setHWLayerAnimListenerIfAlpha(f.mView, anim);
                                        anim.start();
                                    }
                                    container.addView(f.mView);
                                }
                                //...
                                f.onViewCreated(f.mView, f.mSavedFragmentState);
                            }
                        }
    
                        f.performActivityCreated(f.mSavedFragmentState);
                        //...
                    }
                //...
            }
        }
        
        f.mState = newState;
    }
    

    嗯,和我们猜的一样,最后还是调用了这个 moveToState 方法,因为现在我们的参数 newState = Fragment.ACTIVITY_CREATEDf.newState = Fragment.CREATEDf.mFromLayout = false(默认),那从代码一步一步来看这里做了几件事。

    1. 通过 ID 取出装载 Fragment 的容器并赋给 container(看到这里是不是很激动)
    2. 通过调用 f.performCreateView 获取到 View 并赋给 f.mView,那么 performCreateView 又是何方神圣?
    //Fragment
    View performCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        //...
        return onCreateView(inflater, container, savedInstanceState);
    }
    

    哈哈,调用了我们 Fragment 的生命周期方法 onCreateView 方法!!

    1. 然后当然是分别判断 f.mViewcontainer 是否为非空以及是否有动画,有动画的话就启动动画,可以看到,这里是属性动画。最后就将 f.mView 添加到 container 中啦。
    2. 我们下一个生命周期方法就要来啦,去看看 f.performActivityCreated
    //Fragment
    void performActivityCreated(Bundle savedInstanceState) {
        //...
        onActivityCreated(savedInstanceState);
        //...
    }
    

    onActivityCreated 终于出现啦,就这样,我们需要分析的生命周期方法都出来啦。

    1. 方法最后一样改变了 Fragment 的状态,这次是 f.mState = newState = Fragment.ACTIVITY_CREATED

    II-小结

    通过 Activity 的 performCreateCommon 方法后,做了这么两件事:

    1. 改变了 FragmentManager 的 mCruStateFragment.ACTIVITY_CREATED
    2. 接着 Fragment 的生命周期方法 onCreateView 被调用后将返回的 View 添加到指定容器中,随后就是另一个生命周期方法 onActivityCreated 方法,最后一样还会将 f.mState 改成最新的状态即 Fragment.ACTIVITY_CREATED

    阶段小结

    InStrumentation 对象通过 callActivityOnCreate 方法触发了 Activity 的 performCreate 后,Activity 也负责任地分别触发了 Fragment 的生命周期方法 onAttachonCreateonCreateViewonActivityCreated。经过了上面的分析,现在看这张图是不是不会觉得难记了?


    其它生命周期方法逻辑没多大区别,每当 Activity 的生命周期方法被触发后它也会触发依附在它身上的 Fragment 的生命周期方法,感兴趣的朋友可以亲自去看看源码。

    0x06

    为了方便理解,我画了张思维导图,图片在此(超级大...)。


    我再放上最开始那张类图,现在再看一遍,不知是不是印象深刻多了?

    0x07

    我看过很多作者写的源码解析,但这次是我第一次写源码解析,不知自己能否驾驭得当,不知能否让各位理解整个流程。如有不足,希望能在评论提出,十分感谢哈。如果觉得写得不错,不求打赏,能否稍微给我点个赞呢?

    相关文章

      网友评论

      • 0d8a3db0bcc9:文章写得不错,不过感觉楼主对双向链表有误解
      • TimeJ:流弊,大神啊!:+1:
      • 81c17c76d562:我打断点,BackStackRecord的run并非是直接由mExecCommit调用,而是由activity的onstart的execPendingActions调用,我猜测这两个地方都有机会被调用,这个取决于onstart和mExecCommit这个runnable在mainMessagequeue中的前后,而不是文中说的那样。也就解释了我下面一条的留言。同时在最新的support v7中,26.0.0(貌似是25以后)对fragment的流程又进行了一些改变,25.1.1还支持了FragmentTransaction.setAllowOptimization(true).更加复杂。看fragment的代码真的恶心
        81c17c76d562:又翻了源码,我觉得在基本没有机会调用mExecCommit中的execPendingActions,因为在activity的oncreate之后紧接着就是onstart,并没有走ipc,所以我上面说『这个取决于onstart和mExecCommit这个runnable在mainMessagequeue中的前后』是有问题的。如果有错误,还请指出,谢谢
        81c17c76d562:说到底,fragment就是个有生命周期的view,我还不如直接自己封装个简单的。在25.2.x的时候fragment还有个巨大的回退栈bug,谷歌修复了但是没在文档中说。https://stackoverflow.com/questions/42643952/fragment-back-stack-issue-in-build-version-25-2-0
      • 81c17c76d562:现在的FragmentActivity中真正开始fragment操作是在onStart中了,也就是执行完activity的onCreate还没开始fragment的attach,但是为什么要这么改呢?
      • wodezhuanshu:写的确实不错 很好
      • ef727e3aaf69:看了几遍,收获一次比一次大!
      • 7650769b4b02:博主,你好,你写到"先切换到主线程后先后调用了 Fragment 的生命周期方法 onAttach、onCreate 并将 Fragment 的状态即 mState 改为了 Fragment.CREATED",请问是从哪个子线程切换到的主线程?
        7650769b4b02:@州轴走宙 谢谢博主讲解,之前没明白为什么还要有mExecCommit这个runnable,没有想到需要在子线程去操作fragment,谢谢,已打赏
        __huazhou:@70kg 这里的重点不是从哪个子线程切换,而是你可以在子线程对 Fragment 进行操作,它会自动跳回主线程处理
      • xingstarx:@YoKey 好任性。。多打赏点。。 博文写的炒鸡好。。我还要再拜读三遍
      • AndyJue:哥们,写的太好了,获益匪浅

      本文标题:通过源码解析 Fragment 启动过程

      本文链接:https://www.haomeiwen.com/subject/hvlalttx.html