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> 队列。