尊重原创:http://blog.csdn.net/yuanzeyao/article/details/52895029
如果你喜欢我的文章,可以关注左边微信公众号,我会定期推送文章跟新。
在前面的文章中,我们详细分析了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();
我们发现,向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);
}
看到这里大家就知道FragmentTransaction
的实现类就是BackStackRecord
,我们接下来看看此类的继承关系:
final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, Runnable
你会发现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;
}
原来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);
}
我们接下来仔细分析这个方法:
- 将
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++; }
学过数据结构的同学一眼就可以看出来,就是将
Op
对象加入链表的尾部。ok,add
方法分析完毕,原来add方法仅仅是创建了一个”添加操作”,然后放入链表尾部,接下来,我们分析最后一步:commit方法:
public int commit() {
return commitInternal(false);
}
太简单,不用解释,直接进入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;
}
此方法的逻辑也是非常清晰:首先检查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);
}
}
}
这个方法也很简单,首先检查是否允许状态丢失,这里我们传入的false,所以首先调用checkStateLoss
,此方法我就不分析,其实就是检查onSaveInstanceState
方法是否已经执行,如果执行了,那么会抛出异常。检查通过后将Runnable
加入一个待执行列表mPendingActions
(根据前面的分析我们知道这个Runnable
就是BackStackRecord
对象)。紧接着将mExecCommit
放入Handler
中,其实mExecCommit
也是一个Runnable
,它的run
方法如下:
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
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;
}
其实就是执行BackStackRecord
对吧,所以我们需要进入BackStackRecord
的run方法研究一下,run方法比较长,里面有一个switch语句:
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = enterAnim;
mManager.addFragment(f, false);
} break;
我贴出来的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);
}
}
}
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); }
将
fragment
放入mAdd
列表,由于addFragment
的第二个参数传入的是false,所以此时不会执行moveToState
方法,(moveToState
在前面一篇文章已经详细分析了),那么此方法什么时候执行的呢?大家回到BackStackRecord
的run方法,最后面就会调用此方法mManager.moveToState(mManager.mCurState, transition, transitionStyle, true); if (mAddToBackStack) { mManager.addBackStackState(this); }
OK,到此为止详细
add
操作大家已经非常清楚了,使用同样的分析方法,我们来看看remove
的逻辑,你会发现remove
最终也是调用到了BackStackRecord
的run
方法,只不过对应的是下面的case:
case OP_REMOVE: {
Fragment f = op.fragment;
f.mNextAnim = exitAnim;
mManager.removeFragment(f, transition, transitionStyle);
调用的是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);
}
}
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
) 你们可以自己分析,其实逻辑是一样的,这里我就不分析了。