[commit(), commitNow()和commitAllowingStateLoss()]
關于FragmentTransaction的各種提交方法: commit()
,commitAllowingStateLoss()
,commitNow()
和commitNowAllowingStateLoss()
.
作者Bryan Herbst發(fā)了一個blog The many flavors of commit()討論這幾個方法的特點和用途.
下文是中文摘要.
FragmentTransaction的提交方法
support library的FragmentTransaction
現(xiàn)在提供了四種不同的方法來commit一個transaction:
commit()
commitAllowingStateLoss()
commitNow()
commitNowAllowingStateLoss()
這篇文章分析了這四個方法的不同.
commit() vs commitAllowingStateLoss()
用commit()
提交有時候會遇到IllegalStateException
, 說你在onSaveInstanceState()
之后提交, 這里有另一個文章很好地分析了這個問題:Fragment Transactions & Activity State Loss
commit()
和commitAllowingStateLoss()
在實現(xiàn)上唯一的不同就是當你調用commit()
的時候, FragmentManger會檢查是否已經存儲了它自己的狀態(tài), 如果已經存了, 就拋出IllegalStateException
.
那么如果你調用的是commitAllowingStateLoss()
, 并且是在onSaveInstanceState()
之后, 你可能會丟失掉什么狀態(tài)呢?
答案是你可能會丟掉FragmentManager的狀態(tài), 即save之后任何被添加或被移除的Fragments.
舉例說明:
1.在Activity里顯示一個FragmentA;
2.然后Activity被后臺, onStop()
和onSaveInstanceState()
被調用;
3.在某個事件觸發(fā)下, 你用FragmentB replace FragmentA , 使用的是 commitAllowingStateLoss()
.
這時候, 用戶再返回應用, 可能會有兩種情況發(fā)生:
1.如果系統(tǒng)殺死了你的activity, 你的activity將會重建, 使用了上述步驟2保存的狀態(tài), 所以A會顯示, B不會顯示;
2.如果系統(tǒng)沒有殺死你的activity, 它會被提到前臺, FragmentB就會顯示出來, 到下次Activity stop的時候, 這個包含了B的狀態(tài)就會被存下來.
(上述測試可以利用開發(fā)者選項中的”Don’t Keep Activities”選項).
那么你要選擇哪一種呢? 這就取決于你提交的是什么, 還有你是否能接受丟失.
commit(), commitNow() 和 executePendingTransactions()
使用commit()
的時候, 一旦調用, 這個commit并不是立即執(zhí)行的, 它會被發(fā)送到主線程的任務隊列當中去, 當主線程準備好執(zhí)行它的時候執(zhí)行.
popBackStack()
的工作也是這樣, 發(fā)送到主線程任務隊列中去. 也即說它們都是異步的.
但是有時候你希望你的操作是立即執(zhí)行的, 之前的開發(fā)者會在commit()
調用之后加上 executePendingTransactions()
來保證立即執(zhí)行, 即變異步為同步.
support library從v24.0.0開始提供了 commitNow()
方法, 之前用executePendingTransactions()
會將所有pending在隊列中還有你新提交的transactions都執(zhí)行了, 而commitNow()
將只會執(zhí)行你當前要提交的transaction. 所以commitNow()
避免你會不小心執(zhí)行了那些你可能并不想執(zhí)行的transactions.
但是你不能對要加在back stack中的transaction使用commitNow()
, 即addToBackStack()
和commitNow()
不能同時使用.
為什么呢?
想想一下, 如果你有一個提交使用了commit()
, 緊接著又有另一個提交使用了commitNow()
, 兩個都想加入back stack, 那back stack會變成什么樣呢? 到底是哪個transaction在上, 哪個在下? 答案將是一種不確定的狀態(tài), 因為系統(tǒng)并沒有提供任何保證來確保順序, 所以系統(tǒng)決定干脆不支持這個操作.
前面提過popBackStack()
是異步的, 所以它同樣也有一個同步的兄弟popBackStackImmediate()
.
所以實際應用的時候怎么選擇呢?
- 如果你需要同步的操作, 并且你不需要加到back stack里, 使用
commitNow()
.
support library在FragmentPagerAdapter里就使用了commitNow()來保證在更新結束的時候, 正確的頁面被加上或移除. - 如果你操作很多transactions, 并且不需要同步, 或者你需要把transactions加在back stack里, 那就使用
commit()
. - 如果你希望在某一個指定的點, 確保所有的transactions都被執(zhí)行, 那么使用
executePendingTransactions()
.