選擇正確的 Fragment#commitXXX() 函數
最新版本(v24.0.0)的 Support v4 庫中的 FragmentTransaction 添加了 commitNow() 和 commitNowAllowingStateLoss () 兩個函數,這樣 提交一個 Fragment 就有如下4個函數可以選擇:
– commit()
– commitNowAllowingStateLoss()
另外,在使用 Fragment 的過程中,可能您已經使用過了 executePendingTransactions() 這個函數了。
下面來深入分析下每個函數是干啥用的,你應該使用哪個函數。
commit() vs commitAllowingStateLoss()
大部分使用 Fragment 的開發者可能都遇到過如下的異常:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
Alex Lockwood 寫過一篇文章來詳細解釋為何會出現該異常。但是開發者更多的是想知道他們的應用出現了該問題意味著什么?
commit() 和 commitAllowingStateLoss() 的實現幾乎是一樣的。唯一的區別就是在調用 commit() 的時候 FragmentManager 會檢查是否已經保存了其狀態,如果狀態已經保存了,則就拋出 IllegalStateException 異常。
那么,在 onSaveInstanceState() 函數執行以后,您調用 commitAllowingStateLoss() 會丟失那些狀態呢? 答案就是你可能丟失 FragmentManager 的狀態,這里面包含在 onSaveInstanceState() 執行之后添加和刪除的 Fragment。
例如:
1. 當前您的 Activity 在顯示 FragmentA
2. 您的 Activity 被切換到后臺了((onStop() 和 onSaveInstanceState() 函數被調用了)
3. 這個時候您的 Activity 的邏輯發生了一些變化,您使用 FragmentB 替換了 FragmentA 并調用了 commitAllowingStateLoss() 函數來提交這個變化。
這個時候,當用戶再次切回您的 Activity 的時候可能出現如下兩種狀態:
- 如果系統內存不足并且殺死了您的應用,當用戶重新打開您的 應用的時候,系統將會恢復您的應用到上面第二步的狀態,而 FragmentB 是不會顯示的。
- 如果系統沒有殺死您的應用,用戶則可以看到 FragmentB。當 Activity 再次回到后臺的時候(onStop), FragmentB 的狀態才會被保存起來。
Github 上有個示例項目 演示該情況。在運行該示例的時候,如果打開開發者選項中的 “Don’t Keep Activities” 設置,則可以用來顯示第一種情況,FragmentB 的狀態完全丟失了。 如果沒有打開 “Don’t Keep Activities” 選項,則可以查看第二種情況。
這兩個函數使用哪個取決于您提交的 Fragment 和 該 Fragment 狀態是否重要,如果狀態丟失了也沒關系,則您可以使用 commitAllowingStateLoss() 函數。
commit(), commitNow(), 和 executePendingTransactions()
其他版本的 commit() 指定了提交發生的時機。 commit() 的文檔有如下說明:
安排一個針對該事務的提交。提交并沒有立刻發生,會安排到在主線程下次準備好的時候來執行。 (Schedules a commit of this transaction. The commit does not happen immediately; it will be scheduled as work on the main thread to be done the next time that thread is ready.)
上面文檔說明的意思是,你可以同時執行多次提交,這些提交都沒有立刻執行,知道下次主線程準備好了才一起執行這些提交的 Fragment。這些針對 Fragment 的提交操作包含 添加、刪除、替換以及通過函數 popBackStack() 來返回前一個 Fragment。
有時候,您需要針對 Fragment 的操作立刻執行。之前都是通過在調用函數 commit() 后調用 executePendingTransactions() 來實現的。
在 24.0.0 版本的 Support 庫中添加了 commitNow() 函數來更好的支持這種操作。commitNow() 只同步的執行當前的提交操作,而 executePendingTransactions() 則會執行所有等得執行的操作。 commitNow() 可以避免您執行之前提交的但是無需立刻執行的操作。
commitNow() 有個限制,無法把當前提交的 Fragment 添加到堆棧(back stack)中。假設有如下的情況:
通過 commit() 函數把 Fragment A 添加到堆棧中,然后立刻使用 commitNow() 把另外一個 Fragment B 添加到堆棧中,這樣當前的堆棧應該是何種狀態? 是 Fragment A 在前面還是 Fragment B 在前面呢?
popBackStack() 和 popBackStackImmediate() 與 commit() 和 commitNow() 的情況是一樣的。
最后來總結下該如何選擇這些函數:
- 如果你需要同步提交 Fragment 并且無需添加到 堆棧 中,則使用 commitNow()。 Support 庫中在 FragmentPagerAdapter 中使用這個函數,來確保更新 Adapter 的時候 正確的頁面被添加和刪除了。一般來說,只要不添加到堆棧中,都可以使用這個函數來提交。
- 如果執行了多次提交,并且不需要是同步的,或者把每次提交都添加到 堆棧 中,那么就使用 commit()。
- 如果 您需要把多次提交操作的同一個時間點一起執行,則使用 executePendingTransactions()
From: Bryan Herbst
來自:http://blog.chengyunfeng.com/?p=1016