Fragment事务提交源码分析

注:本文是使用V4包下的Fragment进行分析的。

本节就Fragment中出现的类并梳理各个类之间的关系,以供后续文章中条理更加清晰。

我们既然要对Fragment进行分析,那么我们需要找到相关的类,我们如何找这些类呢??我们可以从核心类开始找起。

操作Fragment的最核心的类是什么?
只要稍微了解过Fragment的人都知道,是FragmentManagerFragmentTransaction两个类,我们需要根据这两个类顺藤摸瓜找到所有相关类。

FragmentManager

为了方便文章写作及篇幅限制,下文称这两个类为FM

本质

我们一直在使用FMFT,那这两个类究竟是什么呢??


我们可以看到FM是个抽象类,类的描述也比较清晰,屁都没说,因为V4包是为了兼容老版本的库,所以我们还需要去android.app包下去看看究竟这个类是做什么的。

好了,这个定义很清晰了,和想象中的一样,真是简洁,两个类都用了一句话介绍。
FragmentManagerFragmentActivity中交互的接口类。

获取过程分析

我们本节对获取FM的过程进行一步步地分析,并获取相关类。

在Activity中获取

FragmentManager fm = mActivity.getSupportFragmentManager();

我们先对Activity中获取FragmentManager的过程进行追踪,看看是如何生成这个对象的。
getSupportFragmentManager()方法

/**
 * Return the FragmentManager for interacting with fragments associated
 * with this activity.
 */
public FragmentManager getSupportFragmentManager() {
    return mFragments.getSupportFragmentManager();
}

这个mFragments对象又是什么呢?我们继续分析。
mFragments的定义

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

我们得到了一个新的类了,FragmentController,听名字就知道是和Fragment挂钩的,我们继续分析。

FragmentController类

Provides integration points with a {@link FragmentManager} for a fragment host.
It is the responsibility of the host to take care of the Fragment’s lifecycle.
The methods provided by {@link FragmentController} are for that purpose.
FragmentController是为一个Fragment host提供FragmentManager的一个类,并且负责Fragment的生命周期。
类的定义如下图

那么,我们从这个类如何得到FM呢?我们看看这个类中的方法及参数。
FragmentController中相关方法及参数

private final FragmentHostCallback<?> mHost;
private FragmentController(FragmentHostCallback<?> callbacks) {
   mHost = callbacks;
}

我们又得到了一个新的类FragmentHostCallback

FragmentHostCallback类

Integration points with the Fragment host.Fragments may be hosted by any object; such as an {@link Activity}. In order to host fragments, implement {@link FragmentHostCallback}, overriding the methods applicable to the host.
Fragment可能被任意对象持有,我们可以在Activity中操作Fragment,也可以在Fragment中操作其他的Fragment,这个类的主要是给这个Fragment的持有者使用的。
该类的定义截图如下


我们现在只关心FragmentManager的获取,我们来看看在知道个类中的相关方法。

final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
FragmentManagerImpl getFragmentManagerImpl() {
    return mFragmentManager;
}

又出现了新的类FragmentManagerImpl,我们下面再看看这个类。

FragmentManagerImpl类

Container for fragments associated with an activity.
Fragment关联Activity的容器。
这是继承FragmentManager的一个类,我们拿到的FragmentManager对象就是这个类的实例。
该类的定义如下图

我们所有的FragmentManager都是通过这个类来完成的。

FragmentTransaction

本节对FragmentTransaction的获取过程进行分析。以下篇幅简称为FT

本质



FragmentTransaction:为Fragment操作提供一系列执行的Api。

获取过程

本节对FT的获取进行分析,并对其中的一些相关类进行说明。

获取代码

FragmentManager fm = mActivity.getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();

在之前分析FragmentManager的过程中,我们已经知道FragmentManager实际上操作的类是FragmentManagerImpl,所以我们直接去看FragmentManagerImpl中的beginTransaction方法。

@Override
public FragmentTransaction beginTransaction() {
  return new BackStackRecord(this);
}

我们得到的这个FM对象实际上是这个BackStackRecord实例,我们下面来看看这个类和FT之间的关系。

BackStackRecord类

Entry of an operation on the fragment back stack.
Fragment返回栈操作的实例。

BackStackRecord类结构图

通过上图可以看到BackStackRecordFT的一个子类,所以在FM中使用这个对象当然没有问题了。

事物的提交过程分析

上节,我们通过源码知道了FragmentManagerFragmentTransaction两个类的具体类了,下面我们追踪一下Fragment的事物提交的过程。我们从开始的beginTransaction到最后的commit方法进行一步步分析。

beginTransaction()方法

从上节我们知道这个方法实际上是返回一个FT对象以供使用,而这个对象其实就是BackStackRecord类的实例,所以我们从这个类开始分析。

构造方法

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

需要传入FragmentManagerImpl对象。

add()方法

public FragmentTransaction add(Fragment fragment, String tag) {
    doAddOp(0, fragment, tag, OP_ADD);
    return this;
}
public FragmentTransaction add(int containerViewId, Fragment fragment) {
    doAddOp(containerViewId, fragment, null, OP_ADD);
    return this;
}
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
    doAddOp(containerViewId, fragment, tag, OP_ADD);
    return this;
}
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);
}

可以看出,最后实际添加了一个Op对象,我们需要知道这个Op类是做什么的,类结构是什么样的。

Op类结构

static final class Op {
    Op next;
    Op prev;
    int cmd;
    Fragment fragment;
    int enterAnim;
    int exitAnim;
    int popEnterAnim;
    int popExitAnim;
    ArrayList<Fragment> removed;
}

看到这个类结构是不是有点熟悉,是的,这个类是一个双向链表结构,存储了fragment的一系列事物操作。我们再看看这个addOp方法。

addOp方法

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

上面的代码很简单,在链表尾部插入一个对象,我们再看看其他事物操作的方法。

事物操作其他方法

可以看到,事物操作的方法都是统一的,都封装到了这个双向链表中,只是执行的操作不同。

commit方法

通过上面的代码,我们知道Fragment的事物操作最终都添加到双向链表中去了,我们接下来看看在提交事物的时候会如何进行操作。

通过源码,可以知道commit方法最终调用了FragmentManager中的enqueueAction方法。我们去FM中查看代码。

enqueueAction方法

在上图我们可以看到,除了在检查状态是否丢失方法checkStateLoss()之外,下面的代码使用了锁关键字synchronized,这说明:Fragment在执行commit的时候可能存在多线程的处理,当然,这在文章《Fragment源码解析(引子)》中也怀疑过是这种处理,只有这样才能合理解释在上篇文章中遇到的问题。

我们一步步地来对这个方法的执行进行解析。

if (mDestroyed || mHost == null) {
    throw new IllegalStateException("Activity has been destroyed");
}

此处的mDestroyedFragmentManagerImpl中的一个全局变量,在dispatchDestroy()方法中会被置为true。而这个方法只会在ActivityonDestroy()中被调用。

mHost指的是管理这个Fragment的类,这个类实际上是FragmentActivity的一个内部类HostCallbacks
HostCallbacks类

我们继续来看下面的方法执行。

if (mPendingActions == null) {
    mPendingActions = new ArrayList<Runnable>();
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
    mHost.getHandler().removeCallbacks(mExecCommit);
    mHost.getHandler().post(mExecCommit);
}

通过mPendingActions的初始化,我们需要注意两个对象,一个是mHost.getHandler()的具体声明位置,一个是mExecCommit
通过源码,mHost.getHandler()的实际对象是在FragmentActivity中声明的。
而这个mExecCommit对象是一个ArrayList<Runnable>对象,只有在mPendingActions的数量为1的时候才会执行进一步操作。我们通过下图来看看具体的源码。

FragmentActivity#mHandler

FragmentManagerImpl#mExecCommit

FragmentManagerImpl#execPendingActions()

我们在execPendingActions()方法中可以看到,这个方法只能在主线程中执行,所以我们是通过Handler.post()方法来实现这一需求。
我们看看这个execPendingActions()方法。

public boolean execPendingActions() {
    ...
    boolean didSomething = false;
    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);
        }           
        mExecutingActions = true;
        //此处把在之前commit时候传入的对象进行了遍历,并且执行了run方法。mPendingActions是ArrayList<Runnable>的实例。
        for (int i=0; i<numActions; i++) {
            mTmpActions[i].run();
            mTmpActions[i] = null;
        }
    }        
    ...
}

我们知道mPendingActions中传入的Runnable对象是在FT#Commit()时候传入的,所以在此处执行run方法的时候,最终调用的是BackStackRecord类中的方法。

BackStackRecord#run()

public void run() {
    if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
    if (mAddToBackStack) {
        if (mIndex < 0) {
            throw new IllegalStateException("addToBackStack() called after commit()");
        }
    }
    bumpBackStackNesting(1);
    TransitionState state = null;
    SparseArray<Fragment> firstOutFragments = null;
    SparseArray<Fragment> lastInFragments = null;
    if (SUPPORTS_TRANSITIONS && mManager.mCurState >= Fragment.CREATED) {
        firstOutFragments = new SparseArray<Fragment>();
        lastInFragments = new SparseArray<Fragment>();
        calculateFragments(firstOutFragments, lastInFragments);
        state = beginTransition(firstOutFragments, lastInFragments, false);
    }
    int transitionStyle = state != null ? 0 : mTransitionStyle;
    int transition = state != null ? 0 : mTransition;
    Op op = mHead;
    while (op != null) {
        int enterAnim = state != null ? 0 : op.enterAnim;
        int exitAnim = state != null ? 0 : op.exitAnim;
        switch (op.cmd) {
            case OP_ADD: {
                Fragment f = op.fragment;
                f.mNextAnim = enterAnim;
                mManager.addFragment(f, false);
            } break;
            case OP_REPLACE: {
                Fragment f = op.fragment;
                int containerId = f.mContainerId;
                if (mManager.mAdded != null) {
                    for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
                        Fragment old = mManager.mAdded.get(i);
                        if (FragmentManagerImpl.DEBUG) Log.v(TAG,
                                "OP_REPLACE: adding=" + f + " old=" + old);
                        if (old.mContainerId == containerId) {
                            if (old == f) {
                                op.fragment = f = null;
                            } else {
                                if (op.removed == null) {
                                    op.removed = new ArrayList<Fragment>();
                                }
                                op.removed.add(old);
                                old.mNextAnim = exitAnim;
                                if (mAddToBackStack) {
                                    old.mBackStackNesting += 1;
                                    if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                                            + old + " to " + old.mBackStackNesting);
                                }
                                mManager.removeFragment(old, transition, transitionStyle);
                            }
                        }
                    }
                }
                if (f != null) {
                    f.mNextAnim = enterAnim;
                    mManager.addFragment(f, false);
                }
            } break;
            case OP_REMOVE: {
                Fragment f = op.fragment;
                f.mNextAnim = exitAnim;
                mManager.removeFragment(f, transition, transitionStyle);
            } break;
            case OP_HIDE: {
                Fragment f = op.fragment;
                f.mNextAnim = exitAnim;
                mManager.hideFragment(f, transition, transitionStyle);
            } break;
            case OP_SHOW: {
                Fragment f = op.fragment;
                f.mNextAnim = enterAnim;
                mManager.showFragment(f, transition, transitionStyle);
            } break;
            case OP_DETACH: {
                Fragment f = op.fragment;
                f.mNextAnim = exitAnim;
                mManager.detachFragment(f, transition, transitionStyle);
            } break;
            case OP_ATTACH: {
                Fragment f = op.fragment;
                f.mNextAnim = enterAnim;
                mManager.attachFragment(f, transition, transitionStyle);
            } break;
            default: {
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
            }
        }
        op = op.next;
    }
    mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
    if (mAddToBackStack) {
        mManager.addBackStackState(this);
    }
}

从上面的方法中可以看到,在执行commit()之后,当Handler中的队列执行到这个Runnable的时候,会调用这个方法,而这里会根据Op对象的行为对FragmengManager进行相关的Fragment操作。

我们就先使用add方法进行查看,我们看看addFragment()方法。

public void addFragment(Fragment fragment, boolean moveToStateNow) {
    if (mAdded == null) {
        mAdded = new ArrayList<Fragment>();
    }
    if (DEBUG) Log.v(TAG, "add: " + fragment);
    makeActive(fragment);
    if (!fragment.mDetached) {
        if (mAdded.contains(fragment)) {
            throw new IllegalStateException("Fragment already added: " + fragment);
        }
        mAdded.add(fragment);//在此处添加到了mAdded中。
        fragment.mAdded = true;
        fragment.mRemoving = false;
        if (fragment.mHasMenu && fragment.mMenuVisible) {
            mNeedMenuInvalidate = true;
        }
        if (moveToStateNow) {
            moveToState(fragment);
        }
    }
}

总结

在本篇文章中,我们针对Fragment提交一次事物操作进行了分析,发现事物的提交时机是由FragmentActivity中的Handle进行决定的,并不是立即执行,所以会造成上篇文章中遇到的错误。本篇文章是为了解决在开源FragmentRigger中遇到的错误而分析的,如果在后面遇到其他问题会再进行相关分析。