文章里所有分析都是根據Android Sdk 25.3.1
在分析棧管理之前先來了解幾個基本的概念和 FragmentManager 中主要屬性代表什么意思。
FragmentManagerImpl
FragmentManagerImpl
是 FragmentManager
的實現類,具體實現了管理 Fragment 的各種操作。
BackStackRecord
BackStackRecord 實現了 FragmentTransaction 是 FragmentManager 執行操作的事務單元,一次 commit 就會產生一個 BackStackRecord 記錄,而在一個 BackStackRecord 記錄中有一個 OP 列表。popBackStack()
方法也會產生一個相應的 BackStackRecord 記錄單元。
OP
OP
是 BackStackRecord 的一個子類,表示一個事務中的一個對 Fragment 具體的操作。里面包含了操作類型、操作對象 Fragment、入棧進入退出動畫、出棧進入退出動畫。
static final class Op {
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
}
具體有下面幾種對 Fragment 的操作:
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;
FragmentManager 變量
mActive 不僅有活著的 fragments 還有在返回棧中等待被復原的 fragments。
mAdded 是
mActive
的子集表示活著的 fragmentsArrayList<OpGenerator>
mPendingActions 待執行動作ArrayList<BackStackRecord>
mTmpRecords
用于優化執行的臨時變量ArrayList<Boolean>
mTmpIsPop
用于優化執行的臨時變量-
ArrayList<Integer>
mAvailIndicesmActive 中可用的索引列表
-
ArrayList<BackStackRecord>
mBackStack返回棧 BackStackRecord的返回棧
ArrayList <BackStackRecord>
mBackStackIndices
返回棧索引列表,用來給 BackStackRecord 分配mIndex
需要。ArrayList<Integer>
mAvailBackStackIndices
返回??捎盟饕斜?/strong>,存儲著mBackStackIndices
列表中值為 null 的下標即可以插入BackStackRecord
。
流程圖
下面是最基本的 add Fragment 的代碼片段:
getSupportFragmentManager().beginTransaction()
.add(R.id.content, fragment)
//.remove(fragment)
//.replace(R.id.content,fragment2)
.commit();
通過 beginTransaction()
獲取一個 FragmentTransaction 然后調用 add()
方法,其實不管是 add()
還是 remove()
,replace()
等方法,最后都是調用的 doAddOp()
方法添加一個 OP 對象到 BackStackRecord.mOps
列表中。然后再調用 commit()
提交該事務。
BackStackRecord.commitInternal()
commit()
方法調用 commitInternal()
方法,在該方法中用來給每個 BackStackRecord 事務分配 mIndex
標識。如果該事務不需要加入返回棧就分配 -1 否則就調用 mManager.allocBackStackIndex(this)
返回標識。
int commitInternal(boolean allowStateLoss) {
if (mCommitted) throw new IllegalStateException("commit already called");
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
BackStackRecord.mIndex 的管理
FragmentManager.allocBackStackIndex()
該方法用于給加入到返回棧的 BackStackRecord 分配 mIndex
下標,分配方法不是持續的遞增而是使用 mBackStackIndices
和 mAvailBackStackIndices
配合避免頻繁為索引列表開辟新的內存空間。從代碼片段中可知,當 mAvailBackStackIndices
為空時說明沒有可用的下標,就直接返回 mBackStackIndices.size()
作為下標并把該 BackStackRecord 添加到 mBackStackIndices
中,反之返回 mAvailBackStackIndices
末尾值作為下標并將 BackStackRecord 添加到該位置。
public int allocBackStackIndex(BackStackRecord bse) {
synchronized (this) {
if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
if (mBackStackIndices == null) {
mBackStackIndices = new ArrayList<BackStackRecord>();
}
int index = mBackStackIndices.size();
if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
mBackStackIndices.add(bse);
return index;
} else {
int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
mBackStackIndices.set(index, bse);
return index;
}
}
}
FragmentManager.freeBackStackIndex()
該方法是出棧的時候調用的,當 BackStackRecord 出棧后來更新返回??捎盟饕斜淼摹S上旅娴拇a可知,是吧相應位置設置成 null,然后把該下標加入可用返回棧列表的列尾。
public void freeBackStackIndex(int index) {
synchronized (this) {
mBackStackIndices.set(index, null);
if (mAvailBackStackIndices == null) {
mAvailBackStackIndices = new ArrayList<Integer>();
}
if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
mAvailBackStackIndices.add(index);
}
}
FragmentManager.setBackStackIndex()
該方法是在 restoreAllState()
方法中調用的,用來處理當 Activity 意外內存重啟后恢復 Fragment 返回棧索引列表(mBackStackIndices)和可用索引列表(mAvailBackStackIndices)的。當傳進來的 index 大于 N 時,遍歷從 N 開始到 index 的位置 add(null) 并把下標添加到 mAvailBackStackIndices
中,反之直接把 BackStackRecord 填充到 index 位置。
public void setBackStackIndex(int index, BackStackRecord bse) {
synchronized (this) {
if (mBackStackIndices == null) {
mBackStackIndices = new ArrayList<BackStackRecord>();
}
int N = mBackStackIndices.size();
if (index < N) {
if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
mBackStackIndices.set(index, bse);
} else {
while (N < index) {
mBackStackIndices.add(null);
if (mAvailBackStackIndices == null) {
mAvailBackStackIndices = new ArrayList<Integer>();
}
if (DEBUG) Log.v(TAG, "Adding available back stack index " + N);
mAvailBackStackIndices.add(N);
N++;
}
if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
mBackStackIndices.add(bse);
}
}
}
FragmentManager.execPendingActions()
在 commitInternal()
中分配完 mIndex
標識后調用 mManager.enqueueAction(this, allowStateLoss)
把 BackStackRecord 添加到 mPendingActions
待執行動作列表中,然后通過 Handler 發送 Runnable 消息到主線程消息隊列中等待被執行。
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
在 Runnable 中執行 execPendingActions()
方法,該方法代碼片段如下。分為幾點來分析:
-
mTmpRecords
,mTmpIsPop
前者用來臨時存儲所有待執行的動作(mPendingActions)生成的 BackStackRecord,后者用來存儲 BackStackRecord 是否為出棧。 -
generateOpsForPendingActions
方法遍歷mPendingActions
調用OpGenerator.generateOps()
方法生成 BackStackRecord 添加到mTmpRecords
并把是否為出棧添加到mTmpIsPop
中,然后調用mPendingActions.clear()
把待執行動作列表清空,因為已經被轉化為 BackStackRecord 列表了。 - 調用
optimizeAndExecuteOps(mTmpRecords, mTmpIsPop)
方法優化并執行 BackStackRecord 列表。 - 最后調用
cleanupExec()
清空列表。
/**
* Only call from main thread!
*/
public boolean execPendingActions() {
ensureExecReady(true);
boolean didSomething = false;
while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
mExecutingActions = true;
try {
optimizeAndExecuteOps(mTmpRecords, mTmpIsPop);
} finally {
cleanupExec();
}
didSomething = true;
}
doPendingDeferredStart();
return didSomething;
}
OpGenerator.generateOps()
關于該接口的實現有兩個地方,即 BackStackRecord.generateOps()
和 PopBackStackState.generateOps()
兩處。前者為 非 pop 出棧,對應 commit()
操作提交的各種類型 BackStackRecord,后者為 pop 出棧,對應 popBackStack()
等方法提交的 PopBackStackState。由下面 BackStackRecord.generateOps() 的實現可知,如果設置了 addToBackStack
是在這里把 BackStackRecord 添加到返回棧 mBackStack
中去的。
//BackStackRecord.generateOps()
@Override
public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Run: " + this);
}
records.add(this);
isRecordPop.add(false);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
return true;
}
PopBackStackState.generateOps() 源碼先不分析,等到出棧的時候再詳細分析。
FragmentManager.optimizeAndExecuteOps()
該方法合并相鄰的允許被優化(mAllowOptimization = true
)的 BackStackRecord 。比如有一個 Transaction 添加到了返回棧,然后另一個 Transaction 是要把前面的 Transaction pop 出棧,這兩個 Transaction 將會被優化。
比如下面的代碼片段,不允許優化時 a1Fragment 被 add 進 FragmentManager,走一遍 Fragment 的生命周期,然后 popBackStack() 再把 Fragment remove 掉。而優化后不會再執行 Fragment 的生命周期就被直接 remove 掉。
getSupportFragmentManager().beginTransaction()
.add(R.id.content, a1Fragment)
.setAllowOptimization(true)
.addToBackStack("a1")
.commit();
getSupportFragmentManager().popBackStack();
FragmentManager.executeOpsTogether()
在該方法中會執行 expandReplaceOps
方法把 replace 替換(目標 fragment 已經被 add )成相應的 remove 和 add 兩個操作,或者(目標 fragment 沒有被 add )只替換成 add 操作。
if (!isPop) {
record.expandReplaceOps(mTmpAddedFragments);
} else {
record.trackAddedFragmentsInPop(mTmpAddedFragments);
}
然后調用 executeOps
方法,根據不同 Op.cmd 調用不同的方法對 fragment 進行操作。我們就先來分析 add 操作的流程。會調用 mManager.addFragment(f, false)
方法。在 addFragment 方法中先執行 makeActive
方法,把 add 進來的 fragment 添加到 mActive
列表中,看這個分配 index 的步驟是不是很熟悉?沒錯和給 BackStackRecord 分配 index 的步驟非常像,即移除 fragment 的時候是把相應下標處置 null,然后把該下標保存在 mAvailIndices
列表中,添加 fragment 的時候就會去這個列表中取出最后一個下標。在 SDK23 版本時因為就因為這種分配方法導致了一個 bug :①添加 A1,B1,C1 三個Fragment。② remove 掉這三個 fragment 并且 add 進去 a2,b2 兩個 fragment,期望是要顯示 b2 但是實際情況確實顯示的 a2。在 Sdk >= 24 中這個問題已經被修復。
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 沒有 Detached 的時候執行下面邏輯,由此可見 mAdded
是 mActive
的一個子集,沒有detached 的 fragment 才會被加入到 mAdded
。
if (!fragment.mDetached) {
if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: " + fragment);
}
mAdded.add(fragment);
...
}
然后執行 moveToState 方法改變 fragment 的狀態(添加 fragment 的不同情況會有所不同,但最后都是要 moveToState 改變 fragment 的狀態的,進而觸發 fragment 的生命周期函數) 。
moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive)
在moveToState() 方法中有下面這段代碼,我查看了 SDK 23 的 moveToState() 方法的源碼是遍歷的 mActive列表里的 fragments,而現在是遍歷的 mAdded,因為由上面提到的bug可知 mActive fragments 有可能已經亂序了所以在 SDK 25里是不會出現亂序的這個 bug 了。
// Must add them in the proper order. mActive fragments may be out of order
if (mAdded != null) {
final int numAdded = mAdded.size();
for (int i = 0; i < numAdded; i++) {
Fragment f = mAdded.get(i);
moveFragmentToExpectedState(f);
if (f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
}
}
}
BackStackRecord 出棧
BackStackRecord 出棧的方法有如下幾個:
- popBackStack()
- popBackStackImmediate()
- popBackStack(int id/String name, int flags)
- popBackStackImmediate(int id/String name, int flags)
popBackStack()
PopBackStackState 實現了 OpGenerator 接口,具體實現如下:
- 參數 records 用來存放出棧的 BackStackRecord
- 參數 isRecordPop 用來存放相應 BackStackRecord 是否為出棧(顯然為 true)
- 參數 name 表示出棧到相應 name 的 BackStackRecord
- 參數 id 表示出棧到相應 id 的 BackStackRecord
- 參數 flags (0 或者 POP_BACK_STACK_INCLUSIVE)
- POP_BACK_STACK_INCLUSIVE 如果參數
flags ==POP_BACK_STACK_INCLUSIVE
并且設置了 name 或者 id 那么,所有符合該 name 或者 id 的 BackStackRecord 都將被匹配,直到遇到一個不匹配的或者到達了棧底,然后出棧所有 BackStackRecord 直到最終匹配到的下標位置。否則只匹配第一次 name 或者 id 相符的 BackStackRecord,然后出棧所有 BackStackRecord 直到但不包括匹配到的下標位置。
boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
String name, int id, int flags) {
if (mBackStack == null) {
return false;
}
if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {
int last = mBackStack.size() - 1;
if (last < 0) {
return false;
}
records.add(mBackStack.remove(last));
isRecordPop.add(true);
} else {
int index = -1;
if (name != null || id >= 0) {
// If a name or ID is specified, look for that place in
// the stack.
index = mBackStack.size()-1;
while (index >= 0) {
BackStackRecord bss = mBackStack.get(index);
if (name != null && name.equals(bss.getName())) {
break;
}
if (id >= 0 && id == bss.mIndex) {
break;
}
index--;
}
if (index < 0) {
return false;
}
// flags&POP_BACK_STACK_INCLUSIVE 位與運算
if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
index--;
// Consume all following entries that match.
while (index >= 0) {
BackStackRecord bss = mBackStack.get(index);
if ((name != null && name.equals(bss.getName()))
|| (id >= 0 && id == bss.mIndex)) {
index--;
continue;
}
break;
}
}
}
if (index == mBackStack.size()-1) {
return false;
}
for (int i = mBackStack.size() - 1; i > index; i--) {
records.add(mBackStack.remove(i));
isRecordPop.add(true);
}
}
return true;
}
如果 返回棧 mBackStack 為空就終止出棧操作并返回 false,當name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0
(調用的是popBackStack()方法)時,把返回棧最后一個 BackStackRecord出棧。當 name 或者 id 被指定的時候,倒序遍歷 mBackStack
,如果遇到 name 或者 id 相符就退出循環,此時 index 為第一次匹配到的下標,如果flags==POP_BACK_STACK_INCLUSIVE
繼續遍歷返回棧,直至棧底或者遇到不匹配的跳出循環。最后出棧所有 BackStackRecord。
popBackStack() 方法調用 enqueueAction 方法,添加出棧動作操作到隊列中,這樣又返回到 BackStackRecord 入棧流程那里的 generateOpsForPendingActions() 方法,分別調用 BackStackRecord.generateOps(),PopBackStackState.generateOps() 實現。