Fragment运行机制源码分析(二)
如果你喜欢我的文章,可以关注左边微信公众号,我会定期推送文章跟新。
在前面的文章中,我们详细分析了Fragment的生命周期,以及讲解了Activity如何控制Fragment的生命周期,按照计划,本篇文章就来分析一下add,replace,remove,hide,show等api到底做了生命逻辑,如果大家还没有阅读Fragment运行机制分析(一),那么建议先阅读一下。
在具体分析源码之前,我们来看看平时我们是如何使用这些api的,已add为例:
FragmentManager mFragmentManager = getSupportFragmentManager();
mFragmentManager.beginTransaction()
.add(R.id.fl_container,HomeFragment.newInstance(), HomeFragment.class.getSimpleName())
.commit();
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
我们发现,向Activity中添加一个Fragment非常简单,就4步:
- 获取Activity中的
FragmentManager,通过上篇文章分析,我们知道返回的是FragmentManagerImpl对象. - 调用
beginTransaction()返回一个FragmentTransaction,它仅仅是一个抽象类,后面我们会分析一下它的子类. - 调用add方法,第一个参数的Fragment将要加入的容器id,第二个参数是要添加的Fragment,第三个参数是此Fragment的tag,设置了这个tag之后,后面就可以通过方法
findFragmentByTag方法找到此Fragment,主要用于Fragment的自动恢复. -
执行
commit方法.同理
replace,remove,hide,show等api的调用方式都是一模一样(当然有时也可能会调用addToBackStack).
接下来,我们从第2步开始研究每一步做了什么,进入FragmentManagerImpl,查看beginTransaction 方法逻辑:
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
看到这里大家就知道FragmentTransaction 的实现类就是BackStackRecord,我们接下来看看此类的继承关系:
final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, Runnable
- 1
- 2
- 1
- 2
你会发现BackStackRecord 不仅继承了FragmentTransaction 类,而且还实现了Runnable 接口,说明BackStackRecord 应该是一个线程.
在第2步拿到了FragmentTransaction 之后就调用add 方法,我们进入BackStackRecord 的add方法:(Note:add 方法有多个重载,但是里面的逻辑基本一致,所以我只分析其中一个)
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
原来add 方法仅仅调用了一个doAddOp 方法,我们通过这个名字可以猜想一下,这个此方法的功能:添加一个Add 操作。add 操作返回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);
}
- 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
- 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
我们接下来仔细分析这个方法:
- 将
FragmentManagerImpl对象保存到Fragment.mFragmentManager中. - 检查
Fragment是否设置过tag,如果设置过,并且和参数中的tag不一致,那么抛出异常。如果没有设置过,那么保存tag. - 检查
Fragment,如果Fragment中的containerId已经不等于0,并且和参数的containerId不一致,那么抛出异常,如果等于0,那么保存containerId - 创建
Op对象,在此对象保存cmd参数和Fragment参数,这里cmd 的值为OP_ADD. -
调用
addOp方法,并将上面创建的Op对象传入.我们顺藤摸瓜,进入
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++; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
学过数据结构的同学一眼就可以看出来,就是将
Op对象加入链表的尾部。ok,add方法分析完毕,原来add方法仅仅是创建了一个”添加操作”,然后放入链表尾部,接下来,我们分析最后一步:commit方法:
public int commit() {
return commitInternal(false);
}
- 1
- 2
- 3
- 1
- 2
- 3
太简单,不用解释,直接进入commitInternal 方法
int commitInternal(boolean allowStateLoss) {
if (mCommitted) throw new IllegalStateException("commit already called");
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = new PrintWriter(logw);
dump(" ", null, pw, null);
}
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
此方法的逻辑也是非常清晰:首先检查commit 方法是否已经执行过,如果多次执行此commit 方法就会抛出异常。判断是否调用过addBackToStack (我们这里没有调用),如果调用了那么需要由FragmentManager 分配一个index,否则返回-1,最后调用mManager.enqueueAction(this, allowStateLoss); 方法.
这里我们看到如果在调试阶段,我们想看FragmentManager的相关Log,我们可以调用FragmentManager的enableDebugLogging方法将Log打开,并通过FragmentManager过滤.
接下来进入FragmentManagerImpl.enqueueAction 方法看看.
public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
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);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
这个方法也很简单,首先检查是否允许状态丢失,这里我们传入的false,所以首先调用checkStateLoss,此方法我就不分析,其实就是检查onSaveInstanceState 方法是否已经执行,如果执行了,那么会抛出异常。检查通过后将Runnable 加入一个待执行列表mPendingActions(根据前面的分析我们知道这个Runnable 就是BackStackRecord 对象)。紧接着将mExecCommit 放入Handler 中,其实mExecCommit 也是一个Runnable ,它的run 方法如下:
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
run 方法就是调用了execPendingActions 方法,逻辑如下:
/**
* Only call from main thread!
*/
public boolean execPendingActions() {
if (mExecutingActions) {
throw new IllegalStateException("Recursive entry to executePendingTransactions");
}
if (Looper.myLooper() != mHost.getHandler().getLooper()) {
throw new IllegalStateException("Must be called from main thread of process");
}
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;
for (int i=0; i<numActions; i++) {
mTmpActions[i].run();
mTmpActions[i] = null;
}
mExecutingActions = false;
didSomething = true;
}
if (mHavePendingDeferredStart) {
boolean loadersRunning = false;
for (int i=0; i<mActive.size(); i++) {
Fragment f = mActive.get(i);
if (f != null && f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
}
}
if (!loadersRunning) {
mHavePendingDeferredStart = false;
startPendingDeferredFragments();
}
}
return didSomething;
}
- 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
- 52
- 53
- 54
- 55
- 56
- 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
- 52
- 53
- 54
- 55
- 56
其实就是执行BackStackRecord 对吧,所以我们需要进入BackStackRecord 的run方法研究一下,run方法比较长,里面有一个switch语句:
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = enterAnim;
mManager.addFragment(f, false);
} break;
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
我贴出来的case就是对应的OP_ADD操作的api,通过前面分析可以知道,当我们执行了add 操作之后,最终调用的是FragmentManager.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);
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
if (moveToStateNow) {
moveToState(fragment);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
addFragment 的主要工作如下:
-
将当前
fragment放入mActivie列表并分配一个index,这个功能是通过调用makeActive实现void makeActive(Fragment f) { if (f.mIndex >= 0) { return; } if (mAvailIndices == null || mAvailIndices.size() <= 0) { if (mActive == null) { mActive = new ArrayList<Fragment>(); } f.setIndex(mActive.size(), mParent); mActive.add(f); } else { f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent); mActive.set(f.mIndex, f); } if (DEBUG) Log.v(TAG, "Allocated fragment index " + f); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
-
将
fragment放入mAdd列表,由于addFragment的第二个参数传入的是false,所以此时不会执行moveToState方法,(moveToState在前面一篇文章已经详细分析了),那么此方法什么时候执行的呢?大家回到BackStackRecord的run方法,最后面就会调用此方法mManager.moveToState(mManager.mCurState, transition, transitionStyle, true); if (mAddToBackStack) { mManager.addBackStackState(this); }- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
OK,到此为止详细
add操作大家已经非常清楚了,使用同样的分析方法,我们来看看remove的逻辑,你会发现remove最终也是调用到了BackStackRecord的run方法,只不过对应的是下面的case:
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = exitAnim;
mManager.removeFragment(f, transition, transitionStyle);
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
调用的是FragmentManager.removeFragment 方法:
public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
final boolean inactive = !fragment.isInBackStack();
if (!fragment.mDetached || inactive) {
if (mAdded != null) {
mAdded.remove(fragment);
}
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
fragment.mAdded = false;
fragment.mRemoving = true;
moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
transition, transitionStyle, false);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
removeFragment首先检查此fragment是否调用过addBackToStack,如果没有调用则inactive为true-
检查
fragment.mDetached或者inactive是否为true,如果为true,那么将fragment从mAdded列表中删除,并调用moveToState方法这里可能有些同学会有疑问:
moveToState会不会执行两次:BackStackRecord.run和removeFragmnet方法都会调用,但是如果你仔细分析,你会发现当removeFragment执行之后,Fragment会从mActive移除,所以第二次执行moveToState的时候发现Fragment为null,所以什么都不干。
至于其他的api(replace,hide ,show) 你们可以自己分析,其实逻辑是一样的,这里我就不分析了。

浙公网安备 33010602011771号