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