Fragment 的生成方式有两种,一种是写在layout文件里的, 如:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:id="@+id/titles" android:layout_width="match_parent" android:layout_height="match_parent" android:name="play.apilearn.HistoryActivity$TitlesFragment" /> </FrameLayout>
另一种是在运行时动态加载:
FragmentManager fm = getFragmentManager(); FragmentManager.enableDebugLogging(true); int count = fm.getBackStackEntryCount(); Log.d(LOG_TAG," backstack size:" + count); //fm.getBackStackEntryAt(0).getName(); FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.fullscreen_content, BlankFragment.newInstance(null, null)); ft.addToBackStack(null); ft.commit();
先看第二种是如何实现的。
首先明确几个关键的类,FragmentManager的继承类(实现类)是FragmentManagerImp, FragmentTransaction的实现类是BackStackRecord。
在FragmentManagerImp中 beginTransaction方法返回了BackStackRecord实例:
@Override public FragmentTransaction beginTransaction() { return new BackStackRecord(this); }
接下来我们来看看这个 事务 做了什么:
FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.fullscreen_content, BlankFragment.newInstance(null, null)); ft.addToBackStack(null); ft.commit();
BackStackRecord不单继承了 FragmentTransaction ,同时实现了BackStackEntry 和Runnable 接口;
实现BackStackEntry 是为了让Fragmentmanager管理, 实现Runnable 是为了把BackStackRecord对象作为消息post到UI线程的消息队列中。backStackRecord是一个事务,该事务中可以执行一个或多个操作(add、remove等), 这些操作的实际执行都是在UI主线程中进行的。这些操作保存在一个双向链表中, 链表的头和尾分别是 mHead 和 mTail
final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable { final FragmentManagerImpl mManager; // operation types static final int OP_NULL = 0; static final int OP_ADD = 1; static final int OP_REPLACE = 2; static final int OP_REMOVE = 3; static final int OP_HIDE = 4; static final int OP_SHOW = 5; static final int OP_DETACH = 6; static final int OP_ATTACH = 7; static final class Op { Op next; Op prev; int cmd; //该操作的目标fragment Fragment fragment; int enterAnim; int exitAnim; int popEnterAnim; int popExitAnim; //对于 OP_REPLACE 操作,removed表示该操作中移除的fragment,它们和目标fragment共享一个containerId ArrayList<Fragment> removed; } ... Op mHead; Op mTail; ... boolean mAddToBackStack; boolean mAllowAddToBackStack = true; String mName; boolean mCommitted; int mIndex = -1;
下面看这行代码做了什么:
ft.replace(R.id.fullscreen_content, BlankFragment.newInstance(null, null));
定位到BackStackRecord的 replace 方法:
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; }
然后定位到 doAddOp 方法,注意操作类型 opcmd 的值是 OP_REPLACE,该值被记录在Op对象中:
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); }
addOp又做了什么呢? 注意BackStackRecord是一个事务,可以包含有序的多个操作,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++; }
接下来看addToBackStack( String name ):
ft.addToBackStack(null); // BackStackRecord.java public FragmentTransaction addToBackStack(String name) { if (!mAllowAddToBackStack) { throw new IllegalStateException( "This FragmentTransaction is not allowed to be added to the back stack."); } mAddToBackStack = true; mName = name; return this; }
可以看到该方法只是 把 mAddToBackStack 标识置位true, 该标识在 FragmentManager执行BackStackRecord中记录的操作时会用到。
最后是commit :
public int commit() { return commitInternal(false); } public int commitAllowingStateLoss() { return commitInternal(true); } 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 调用了内部方法 commitInternal(..) , 在 commitInternal方法返回之前,调用了FragmentManagerImp的enqueAction, 这个action对象就是当前的BackStackRecord对象(用this表示),它是一个事务, 前面说过, BackStackRecord实现了 Runnable 接口,作为一个action添加到FragmentManager的 pendingActions队列中。 fragmentManager会向UI主线程消息队列里post一个执行消息(mExecCommit)。当消息被主线程取出执行(comsume)的时候,会执行pendingActions队列里的每一个action的run方法,所以接下来应该看 BackStackRecord 的run()方法里到底做了什么,不难猜测,当然是之前记录在 Op中的一系列(也可能是一个)操作。
//FragmentManager.java Runnable mExecCommit = new Runnable() { @Override public void run() { execPendingActions(); } }; .... /** * Adds an action to the queue of pending actions. * * @param action the action to add * @param allowStateLoss whether to allow loss of state information * @throws IllegalStateException if the activity has been destroyed */ public void enqueueAction(Runnable action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (mDestroyed || mActivity == null) { throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<Runnable>(); } mPendingActions.add(action); if (mPendingActions.size() == 1) { mActivity.mHandler.removeCallbacks(mExecCommit); mActivity.mHandler.post(mExecCommit); } } } ... /** * Only call from main thread! */ public boolean execPendingActions() { if (mExecutingActions) { throw new IllegalStateException("Recursive entry to executePendingTransactions"); } if (Looper.myLooper() != mActivity.mHandler.getLooper()) { throw new IllegalStateException("Must be called from main thread of process"); } ... 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(); mActivity.mHandler.removeCallbacks(mExecCommit); } mExecutingActions = true; for (int i=0; i<numActions; i++) { mTmpActions[i].run(); // 执行BackStackRecord 对象的run 方法 mTmpActions[i] = null; } mExecutingActions = false; didSomething = true; } ... }
另外这里用到了 mAddToBackStack标识, 若为TRUE, fragmentManager会给当前事务分配一个 index, 注意是在commit ()分配的, 如果你在commit后又调用了 addToBackStack(String name ) 方法,那么mAddToBackStack 标识会被置位true, 但是却没有分配到index (默认 -1) 。当消息被执行时 (在事务的run方法中), mIndex 和 mAddToBackStack 同时被检查,如果 mAddToBackStack 为true 而 mIndex 小于 0,
事务会抛出 IllegalStateException 。这是实现事务的一种安全检查。
从 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); Op op = mHead; while (op != null) { switch (op.cmd) { case OP_ADD: { Fragment f = op.fragment; f.mNextAnim = op.enterAnim; mManager.addFragment(f, false); } break; case OP_REPLACE: { Fragment f = op.fragment; 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 (f == null || old.mContainerId == f.mContainerId) { if (old == f) { op.fragment = f = null; } else { if (op.removed == null) { op.removed = new ArrayList<Fragment>(); } op.removed.add(old); old.mNextAnim = op.exitAnim; if (mAddToBackStack) { old.mBackStackNesting += 1; if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " + old + " to " + old.mBackStackNesting); } mManager.removeFragment(old, mTransition, mTransitionStyle); } } } } if (f != null) { f.mNextAnim = op.enterAnim; mManager.addFragment(f, false); } } break; case OP_REMOVE: { Fragment f = op.fragment; f.mNextAnim = op.exitAnim; mManager.removeFragment(f, mTransition, mTransitionStyle); } break; case OP_HIDE: { Fragment f = op.fragment; f.mNextAnim = op.exitAnim; mManager.hideFragment(f, mTransition, mTransitionStyle); } break; case OP_SHOW: { Fragment f = op.fragment; f.mNextAnim = op.enterAnim; mManager.showFragment(f, mTransition, mTransitionStyle); } break; case OP_DETACH: { Fragment f = op.fragment; f.mNextAnim = op.exitAnim; mManager.detachFragment(f, mTransition, mTransitionStyle); } break; case OP_ATTACH: { Fragment f = op.fragment; f.mNextAnim = op.enterAnim; mManager.attachFragment(f, mTransition, mTransitionStyle); } break; default: { throw new IllegalArgumentException("Unknown cmd: " + op.cmd); } } op = op.next; } mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true); if (mAddToBackStack) { mManager.addBackStackState(this); } }
最后检查了 mAddToBackStack 标识,mBackStack 是在activity 收到 返回事件时去检查的。
void addBackStackState(BackStackRecord state) { if (mBackStack == null) { mBackStack = new ArrayList<BackStackRecord>(); } mBackStack.add(state); reportBackStackChanged(); }
Ps: 解释下 bumpBackStackNesting(int amt), 这个方法的意思是,如果当前事务要放入activity的回退栈,就在当前事务涉及的fragment的mBackStackNesting字段中记录当前事务处于回退栈的第几项(栈顶是 1),字段mBackStackNesting可以用来判断fragment是否在回退栈中。
//BackStackRecord.java void bumpBackStackNesting(int amt) { if (!mAddToBackStack) { return; } if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this + " by " + amt); Op op = mHead; while (op != null) { if (op.fragment != null) { op.fragment.mBackStackNesting += amt; if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " + op.fragment + " to " + op.fragment.mBackStackNesting); } if (op.removed != null) { for (int i=op.removed.size()-1; i>=0; i--) { Fragment r = op.removed.get(i); r.mBackStackNesting += amt; if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " + r + " to " + r.mBackStackNesting); } } op = op.next; } } //Fragment.java final boolean isInBackStack() { return mBackStackNesting > 0; }
回到 run方法, 可以看到根据相应的操作命令(ADD,REMOVE, REPLACE, HIDE, ATTACH等)调用FragmentManager的相应方法。 并在最后根据当前事务的 mAddToBackStack标识决定是否把当前事务加入FragmentManager的 mBackStack<BackStackRecord> 队列。