很久没去用fragment了,渐渐的开始忘记了。今天恰好遇见同事使用fragment,导致程序发生崩溃,因此我重新对fragment进行了分析。我会通过我解决问题的方式,将我理解fragment的思路,告诉大家。
首先,我先按照自己的理解,写了这样一段代码,用于fragment切换。
FragmentManager mManager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Fragment fragment = manager.findFragmentByTag(tag);
if(fragment!=null){
transaction.attach(fragment);
}else{
transaction.add(R.id.container,fragment,tag);
}
transaction.commit();
上面这段代码,参考了一些资料,自己对这段代码还是抱有一些疑问:
1.FragmentManager是什么?
2.beginTransaction(),如何得到FragmentTransaction的?
3.FragmentTransaction 的attach add这些方法,都是做了什么操作?
然后,我就带着这些疑问,开始翻源码了。
首先,我们先查看FragmentManager:
FragmentManager
public abstract class FragmentManager
FragmentManager是抽象类,定义了很多方法。具体的实现,在于它的内部类
final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory
因此,FragmentManager的操作,大部分都在FragmentManagerImpl中。
我们先看beginTransaction()方法:
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
FragmentTransaction其实是BackStackRecord的实例,我猜想,BackStackRecord是FragmentTransaction的子类,可以肯定,FragmentTransaction的操作,实际上是BackStackRecord的实现的。
不要着急,后面将会分析BackStackRecord。
然后,我们看findFragmentByTag():
public Fragment findFragmentByTag(String tag) {
if (mAdded != null && tag != null) {
// First look through added fragments.
for (int i=mAdded.size()-1; i>=0; i--) {
Fragment f = mAdded.get(i);
if (f != null && tag.equals(f.mTag)) {
return f;
}
}
}
if (mActive != null && tag != null) {
// Now for any known fragment.
for (int i=mActive.size()-1; i>=0; i--) {
Fragment f = mActive.get(i);
if (f != null && tag.equals(f.mTag)) {
return f;
}
}
}
return null;
}
mAdded和mActive,是两个ArrayList<Fragment>,它们的作用,分别是来保存通过transaction.add和所有的fragment,在这个方法中,先对mAdded进行查找,没有查找到,再到mActive中查找,查找tag对应的fragment。如果存在,我们将调用FragmentTransaction的attach方法。
接下来,我们分析这个attach方法。前面说到,FragmentTransaction实例,是有BackStackRecord实例化得到的。因此,我们直接去BackStackRecord查看attach方法,可以发现,BackStackRecord的方法,基本和FragmentTransaction的方法一致。打开attach方法:
public FragmentTransaction attach(Fragment fragment) {
Op op = new Op();
op.cmd = OP_ATTACH;
op.fragment = fragment;
addOp(op);
return this;
}
Op是什么呢?
static final class Op {
Op next;
Op prev;
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
ArrayList<Fragment> removed;
}
Op是静态内部类,通过命名,我们可以知道,BackStackRecord其实是通过链表实现的栈。Op是节点,保存了Fragment的相关信息。 最后,attach方法在addOp(op)上:
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().
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最终调用的是mManager.enqueueAction(this, allowStateLoss);转来转去,最后还是回到了FragmentManager的方法,那我们就看看enqueueAction这个方法吧。
这个方法名称,就是把动作放到队列里面去,事实上,每个BackStackRecord相当于一个动作,就是每次beginTransaction,其实就是在构建一次动作,最后commit就是添加到队列里,然后得到执行。
到最后,执行动作的类,是FragmentManager的execPendingActions方法。
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;
}
看到Looper,我们应该知道动作的处理方式了。然后,后面是个while的死循环,
mTmpActions[i].run();是方法的核心,它的实现来自BackStackRecord。为什么这么说,因为BackStackRecord是通过enqueueAction添加到list中,最后到了
mTmpActions中,所以,mTmpActions[i].run();调用的还是BackStackRecord的run(),我们按照预期的想法,确实在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) {
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=0; i<mManager.mAdded.size(); 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);
}
}
结论就出来了,其实FragmentTransaction调用的方法,还是FragmentManagerImpl的方法。通过上面,一系列的过程,我们发现,通过这样的复杂的设计,才保证了Fragment的调用的正确性。大家,通过我翻查源码的过程,应该也理解了FragmentManager的设计了。是不是很有帮助呢?