Fragment事務流程分析
簡言
簡單的事務使用流程代碼
getSupportFragmentManager().beginTransaction().add(R.id.fl, ftMain).commit();
使用的方法很簡單趣竣,但是Activity是如何實現(xiàn)事務的管理的呢终娃?
我們先上一個簡單的類圖這個里面FragementManagerImpl持有mHost绿鸣,一開始不知道是如何設置的,后來才發(fā)現(xiàn)是通過attachHost方法進行設置的躏吊。
我們一步步去分析這個UML的視圖是如何建立的
從 getSupportFragmentManager() 來看一下是如何獲取的
getSupportFragmentManager()
// FragmentActivity.java
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
public FragmentManager getSupportFragmentManager() {
return mFragments.getSupportFragmentManager();
}
//FragmentController.java
//實際是HostCallbacks對象宴凉。
private final FragmentHostCallback<?> mHost;
public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
return new FragmentController(callbacks);
}
private FragmentController(FragmentHostCallback<?> callbacks) {
mHost = callbacks;
}
public FragmentManager getSupportFragmentManager() {
return mHost.getFragmentManagerImpl();
}
這里可以看到我們的Activity持有FragmentControl對象。而FragmentControl持有了HostCallbacks對象恬口,而HostCallbacks是繼承自FragmentHostCallback的校读。
//FragmentHostCallback.java
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
FragmentManagerImpl getFragmentManagerImpl() {
return mFragmentManager;
}
可以看到這里HostCallback是持有FragmenManagerImpl的。而FragmenManagerImpl繼承FragmentManager
所以其實我們的第一步驟getSupportFragmentManager()是獲取到了一個FragmenManagerImpl對象祖能。
beginTransaction
//FragmentManager.java
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
可以看到這里創(chuàng)建了一個BackStackRecord對象歉秫,對象持有了FragmentManager。這里的BackStackRecord是繼承自FragmentTransaction類的养铸。
到這里我們是獲取獲取到了一個事務記錄類雁芙。也就是BackStackRecord
add
當有了事務 記錄類之后,就可以進行各種事務的記錄信息钞螟,add,remove,replace等等兔甘。我們這里只是跟蹤我們的測試代碼,其他的是有相同的處理機制筛圆。
//BackStackRecord.java
public FragmentTransaction add(int containerViewId, Fragment fragment) {
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
fragment.mFragmentManager = mManager;
//一系列檢測
...
//將當前的操作添加到隊列中
addOp(new Op(opcmd, fragment));
}
//增加到處理隊列中
void addOp(Op op) {
mOps.add(op);
//動畫的處理
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
op.popEnterAnim = mPopEnterAnim;
op.popExitAnim = mPopExitAnim;
}
可以看到裂明,其實BackStackRecord內部維護了一個要執(zhí)行的隊列,當進行事務的提交時太援,肯定是需要挨個將這個隊列進行去除執(zhí)行的闽晦。
commit
現(xiàn)在我們已經準備好了一個隊列了,當所有的事務內部要執(zhí)行的操作處理完后提岔,回通過commit()或者commitNowAllowingStateLoss(),commitNow()來進行提交仙蛉。我們這里跟蹤commit()的執(zhí)行,剩下的留以后再慢慢分析碱蒙。
//BackStackRecord.java
public int commit() {
return commitInternal(false);
}
int commitInternal(boolean allowStateLoss) {
//防止重復提交
if (mCommitted) {
throw new IllegalStateException("commit already called");
}
...
mCommitted = true;
if (mAddToBackStack) {//如果執(zhí)行了addToBackStack,mAddToBackStack才會為true
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
//調用manager的入隊操作
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
commit()主要執(zhí)行了入隊的操作荠瘪。
#FragmentManager.java
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
//加鎖
synchronized (this) {
...
mPendingActions.add(action);
scheduleCommit();
}
}
private void scheduleCommit() {
synchronized (this) {
...
//這里的mHost是Fragment中的HostCallBack,這里的getHandler獲取到的是主線程Handler
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
所以這里的是將要執(zhí)行的BackStackRecord的記錄信息存放到了mPendingActions隊列之中,然后通過主線程的Looper機制來進行調用執(zhí)行execPendingActions()方法赛惩。
execPendingActions
#FragmentManager.java
//只能從主線程調用
public boolean execPendingActions() {
//進行一些線程的檢測哀墓,是否銷毀等的檢測操作
ensureExecReady(true);
boolean didSomething = false;
//generateOpsForPendingActions會將我們的PendingActions中的數(shù)據(jù)取出來,然后存放到臨時的mTmpRecords中
while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
mExecutingActions = true;
try {
removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
} finally {
cleanupExec();
}
didSomething = true;
}
doPendingDeferredStart();
burpActive();
return didSomething;
}
我們看一下generateOpsForPendingActions是如何將數(shù)據(jù)獲取并存放到臨時list中的
#FragmentManager.java
//遍歷循環(huán)喷兼,將我們的PendingActions中的數(shù)據(jù)取出來篮绰,然后存放到臨時的mTmpRecords隊列中
private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,ArrayList<Boolean> isPop) {
boolean didSomething = false;
synchronized (this) {
final int numActions = mPendingActions.size();
for (int i = 0; i < numActions; i++) {
didSomething |= mPendingActions.get(i).generateOps(records, isPop);
}
mPendingActions.clear();
//當隊列中的數(shù)據(jù)已經保存到臨時的records中以后,移除回調
mHost.getHandler().removeCallbacks(mExecCommit);
}
return didSomething;
}
所以是通過循環(huán)季惯,調用了generateOps方法
//BackStackRecord.java
public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Run: " + this);
}
//直接添加到list中
records.add(this);
//記錄當前是非pop操作
isRecordPop.add(false);
if (mAddToBackStack) {//記錄是否是入棧操作
mManager.addBackStackState(this);
}
return true;
}
其實這里面的存放到臨時list中的操作相對來說比較簡單吠各。我們回到主線去看看將數(shù)據(jù)存放到臨時list中以后臀突,又對這些事務做了什么操作。也就是removeRedundantOperationsAndExecute()函數(shù)的作用贾漏。
removeRedundantOperationsAndExecute
//FragmentManager.java
//這里進行了事務的優(yōu)化操作候学,事務的最終執(zhí)行也由executeOpsTogether來執(zhí)行的
private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
//完成以前被推遲但現(xiàn)在已經準備好的事務的執(zhí)行。
executePostponedTransaction(records, isRecordPop);
final int numRecords = records.size();
int startIndex = 0;
for (int recordNum = 0; recordNum < numRecords; recordNum++) {
//是否允許重新排序
final boolean canReorder = records.get(recordNum).mReorderingAllowed;
if (!canReorder) {//如果不允許重排序纵散,那么會獲取這個位置之后的連續(xù)不允許排序的隊列然后批量執(zhí)行
//執(zhí)行之前的所有操作
if (startIndex != recordNum) {
//如果不允許重排序梳码,那么就將之前的所有的操作批量執(zhí)行
executeOpsTogether(records, isRecordPop, startIndex, recordNum);
}
//reorderingEnd會從當前不能重排序的位置開始,遍歷搜索困食,尋找到連續(xù)的不能重排序的pop操作的位置
//然后將recordNum到reorderingEnd的位置進行批量執(zhí)行
int reorderingEnd = recordNum + 1;
if (isRecordPop.get(recordNum)) {
//當前是個pop操作边翁,那么會一直搜索到下一個非pop的位置,然后記錄硕盹。然后會將這些所有的pop操作批量執(zhí)行
while (reorderingEnd < numRecords && isRecordPop.get(reorderingEnd) && !records.get(reorderingEnd).mReorderingAllowed) {
//是pop操作符匾,不允許排序
reorderingEnd++;
}
}
executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
//設置下一個批量執(zhí)行的起始位置
startIndex = reorderingEnd;
recordNum = reorderingEnd - 1;
}
}
if (startIndex != numRecords) {
executeOpsTogether(records, isRecordPop, startIndex, numRecords);
}
}
這里,會將我們的隊列進行切割拆分瘩例,將連續(xù)的允許重新排序和不允許重新排序的分別切割出來啊胶,然后通過executeOpsTogether對隊列進行一個批量的操作。我們看看如何批量處理的垛贤。
//執(zhí)行BackStackRecords列表的一個子集集合焰坪,所有這些子集合要么都允許重新排序,要么都不允許排序聘惦。
private void executeOpsTogether(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
//獲取當前批次是否允許重排序
final boolean allowReordering = records.get(startIndex).mReorderingAllowed;
boolean addToBackStack = false;
if (mTmpAddedFragments == null) {
mTmpAddedFragments = new ArrayList<>();
} else {
mTmpAddedFragments.clear();
}
//保存已經添加的fragment臨時快照
mTmpAddedFragments.addAll(mAdded);
//獲取當前棧頂?shù)膄ragment
Fragment oldPrimaryNav = getPrimaryNavigationFragment();
for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
//獲取事務
final BackStackRecord record = records.get(recordNum);
//判斷事務是否為pop
final boolean isPop = isRecordPop.get(recordNum);
if (!isPop) {//重點方法 不是pop某饰,那么就對事務中的操作進行優(yōu)化
oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
} else {//如果是pop操作,那么就執(zhí)行反向操作善绎。也就是add的反向操作和remove的反向操作
record.trackAddedFragmentsInPop(mTmpAddedFragments);
}
addToBackStack = addToBackStack || record.mAddToBackStack;
}
//這里直接clear了是什么鬼黔漂?難道只是為了進行優(yōu)化用的臨時表?禀酱?
mTmpAddedFragments.clear();
if (!allowReordering) {//不允許排序
//開始轉換
FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
false);
}
//重點方法 炬守,最重要的地方。是commit操作的最終執(zhí)行的地方
executeOps(records, isRecordPop, startIndex, endIndex);
int postponeIndex = endIndex;
if (allowReordering) {
ArraySet<Fragment> addedFragments = new ArraySet<>();
addAddedFragments(addedFragments);
postponeIndex = postponePostponableTransactions(records, isRecordPop,startIndex, endIndex, addedFragments);
makeRemovedFragmentsInvisible(addedFragments);
}
if (postponeIndex != startIndex && allowReordering) {
// need to run something now
FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
postponeIndex, true);
moveToState(mCurState, true);
}
for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
final BackStackRecord record = records.get(recordNum);
final boolean isPop = isRecordPop.get(recordNum);
if (isPop && record.mIndex >= 0) {
freeBackStackIndex(record.mIndex);
record.mIndex = -1;
}
record.runOnCommitRunnables();
}
if (addToBackStack) {
reportBackStackChanged();
}
}
這個方法里面有兩個需要關注的重點剂跟。
- expandOps()函數(shù)减途,對執(zhí)行的事務會進行一個優(yōu)化。
- executeOps()函數(shù)曹洽,用來執(zhí)行最終的commit操作鳍置,將fragment和Activity進行關聯(lián)綁定以及生命周期的同步。
這里我們可以簡單看一下expandOps()函數(shù)中優(yōu)化的邏輯送淆。
//BackStackRecords.java
//對事務中的操作進行優(yōu)化處理
Fragment expandOps(ArrayList<Fragment> added, Fragment oldPrimaryNav) {
for (int opNum = 0; opNum < mOps.size(); opNum++) {
final Op op = mOps.get(opNum);
switch (op.cmd) {
case OP_ADD:
case OP_ATTACH://如果是add或者attach墓捻,將當前fragment放入到added中
added.add(op.fragment);
break;
case OP_REMOVE:
case OP_DETACH: {//如果是移除操作,那么從added隊列中移除fragment
added.remove(op.fragment);
if (op.fragment == oldPrimaryNav) {//如果移除的fragment是當前的oldPrimaryNav,需要做一個特殊處理砖第,這里有點不太明白,以后再研究
mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, op.fragment));
opNum++;
oldPrimaryNav = null;
}
}
break;
case OP_REPLACE: {//如果是replace操作环凿,那么就將對應的mContainerId中的fragment移除梧兼,然后再添加當前的fragment
final Fragment f = op.fragment;
final int containerId = f.mContainerId;//獲取到當前fragment占用的containerId信息
boolean alreadyAdded = false;
for (int i = added.size() - 1; i >= 0; i--) {
final Fragment old = added.get(i);
if (old.mContainerId == containerId) {//如果已經添加的fragment的containerId和當前containerId是相同的,那么說明其占用的是同一個containerId智听,需要將added里面的移除
if (old == f) {//如果這個fragment已經在added列表中羽杰,存在,設置一個標記位到推。
alreadyAdded = true;
} else {//將所有其他使用這個containerId的fragment都執(zhí)行一個remove操作考赛。這里是通過向mOps中增加新的操作來進行處理的,而不是直接移除莉测。是因為在遍歷列表的時候颜骤,不能執(zhí)行移除操作
// This is duplicated from above since we only make
// a single pass for expanding ops. Unset any outgoing primary nav.
if (old == oldPrimaryNav) {
mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, old));
opNum++;
oldPrimaryNav = null;
}
final Op removeOp = new Op(OP_REMOVE, old);
removeOp.enterAnim = op.enterAnim;
removeOp.popEnterAnim = op.popEnterAnim;
removeOp.exitAnim = op.exitAnim;
removeOp.popExitAnim = op.popExitAnim;
mOps.add(opNum, removeOp);
added.remove(old);
opNum++;
}
}
}
if (alreadyAdded) {//表明這個opNum位置的fragment已經在add列表中存在了,那么其實不需要再執(zhí)行這個replace命令了捣卤,直接將這個replace操作移除掉忍抽,這樣可以達到優(yōu)化效果
mOps.remove(opNum);
opNum--;
} else {//如果不存在,證明這個fragment之前是不存在的董朝,需要對它執(zhí)行變?yōu)橐粋€add操作
op.cmd = OP_ADD;
added.add(f);
}
}
break;
case OP_SET_PRIMARY_NAV: {
// It's ok if this is null, that means we will restore to no active
// primary navigation fragment on a pop.
mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, oldPrimaryNav));
opNum++;
// Will be set by the OP_SET_PRIMARY_NAV we inserted before when run
oldPrimaryNav = op.fragment;
}
break;
}
}
return oldPrimaryNav;
}
這里注釋很詳細鸠项,那么我們回到主線,看一下executeOps()函數(shù)的執(zhí)行子姜。
//FragmentManager.java
//執(zhí)行操作
private static void executeOps(ArrayList<BackStackRecord> records,ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
for (int i = startIndex; i < endIndex; i++) {
final BackStackRecord record = records.get(i);
final boolean isPop = isRecordPop.get(i);
if (isPop) {//如果是pop操作
//所有的管理的fragment后面的堆棧計數(shù)器-1
record.bumpBackStackNesting(-1);
// Only execute the add operations at the end of
// all transactions.
boolean moveToState = i == (endIndex - 1);
//執(zhí)行pop操作
record.executePopOps(moveToState);
} else {//如果是非pop操作
//所有的管理的fragment后面的堆棧計數(shù)器+1
record.bumpBackStackNesting(1);
//執(zhí)行ops操作
record.executeOps();
}
}
}
這里先對fragment中的表明后面的片段計數(shù)器進行了更新祟绊。然后根據(jù)是否是pop,分別執(zhí)行了executePopOps和executeOps方法哥捕,我們這里只先跟蹤一下executeOps()這個方法的執(zhí)行牧抽。
//BackStackRecords.java
//執(zhí)行事務包含的操作,只有不允許優(yōu)化的時候扭弧,fragment的狀態(tài)才會被改變
void executeOps() {
final int numOps = mOps.size();
for (int opNum = 0; opNum < numOps; opNum++) {
final Op op = mOps.get(opNum);
final Fragment f = op.fragment;
if (f != null) {//設置動畫
f.setNextTransition(mTransition, mTransitionStyle);
}
switch (op.cmd) {//通過manager來進行fragment的管理工作
case OP_ADD:
f.setNextAnim(op.enterAnim);
mManager.addFragment(f, false);//這里我們隊addFragment來進行一個跟蹤處理
break;
case OP_REMOVE:
f.setNextAnim(op.exitAnim);
mManager.removeFragment(f);
break;
case OP_HIDE:
f.setNextAnim(op.exitAnim);
mManager.hideFragment(f);
break;
case OP_SHOW:
f.setNextAnim(op.enterAnim);
mManager.showFragment(f);
break;
case OP_DETACH:
f.setNextAnim(op.exitAnim);
mManager.detachFragment(f);
break;
case OP_ATTACH:
f.setNextAnim(op.enterAnim);
mManager.attachFragment(f);
break;
case OP_SET_PRIMARY_NAV:
mManager.setPrimaryNavigationFragment(f);
break;
case OP_UNSET_PRIMARY_NAV:
mManager.setPrimaryNavigationFragment(null);
break;
default:
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
if (!mReorderingAllowed && op.cmd != OP_ADD && f != null) {
mManager.moveFragmentToExpectedState(f);
}
}
if (!mReorderingAllowed) {
// Added fragments are added at the end to comply with prior behavior.
//對FragmentManager所有管理的fragment進行一次生命周期的同步
mManager.moveToState(mManager.mCurState, true);
}
}
這里是通過FragmentManager來對事務中的fragment來進行了管理阎姥,然后最后通過狀態(tài)的同步,將本次事務的所有變化的fragment的生命周期和綁定的activity的生命周期進行一次同步鸽捻。
總結
每一次學習都要有新的收獲呼巴,這次通過學習fragment的事務執(zhí)行的方法學到了新的知識點
- 事務的提交并不是直接就執(zhí)行,而是通過主線程的Handler機制來執(zhí)行的御蒲。我們經常遇到的add去顯示的時候衣赶,有一個isAdd的崩潰,哪怕我們先進行了isAdd的判斷厚满,也攔截不住府瞄,就是因為Handler執(zhí)行時,從隊列取出消息,如果這時候還沒有執(zhí)行遵馆,那么isAdd就仍然是空鲸郊。遇到過一種解決方案就是glide的,在add之前货邓,將創(chuàng)建的fragment存放到緩存中秆撮,然后commit之后,再發(fā)送一個handler消息换况,從緩存中移除即可职辨。
- 事務的執(zhí)行是有優(yōu)化的,會將一些能夠優(yōu)化的地方進行處理戈二。
- 對于fragment的管理舒裤,是通過FragmentManager來統(tǒng)一進行的。
- 當有多個事務時觉吭,會將事務按照是否能夠優(yōu)化和是否為pop來進行分批腾供,進行分批處理。
本篇文章由一文多發(fā)平臺ArtiPub自動發(fā)布