Fragment的原理和優化
fragment 管理框架升級到了 0.1.0, 添加了一個新的介面 startFragmentAndDestroyCurrent
, 可以方便的完成類似 startActivity
之後 finish 的效果
FragmentTransition#addToBackStack 的誤用
之前也有人在 issue 中問道如何實現類似 Activity#finish()
的效果,我不加思索的回答到:
FragmentTransition
有提供addToBackStack(boolean)
方法, 啟動前一個 fragment (不是新的那個)時,addToBackStack
傳參為 false 就行。
這樣初看也確實解決了問題,一切看似完美,直到另一個 issue 的出現。在閱讀原始碼來解決這個問題時,我發現了 addToBackStack(false)
是存在問題的,這篇文章會指出問題所在,然後給出 QMUI 的解決方案。
BackStack 的工作機制
在之前的文章我也有提到, BackStack 並不是將 fragment 儲存到堆疊,而是將操作(add, remove等)儲存到堆疊,然後在返回時將操作逆著來就行。
在開始之前,先介紹 Fragment 的一個私有成員變數:
Fragment
存在一個成員變數 mBackStackNesting
,它是標誌 fragment 是否存在於 BackStack 的重要變數,每個 fragment 的每次操作都會影響到它, 只有它的值小於等於0時,fragment 才會走到 onDestroy,從而得到釋放
一般情況下,我們切換 fragment 時 BackStack 的變更行為為:
現在,讓我們來看看使用 addToBackStack(false)
時會發生什麼:
回到之前提到的 issue。其出錯的原因是 findCurrentFragment
出錯,而 findCurrentFragment
會先在被新增到 FragmentManager 中的 fragment 佇列中去尋找,根據上面的描述,addToBackStack(false)
會導致頁面存在多個 fragment, 所以猜測可能是它導致的問題,但我也不能確定我的猜測 100% 正確,因為我並沒能在開發環境中重現。
QMUI 的 fix 方案。
現在來說說 QMUI 的 fix 方案。其實也很簡單,我們直接對 BackStack 的最後一個 Entity 做一些修正(以上圖為例):
- 修改 Op[addB] 替換為 Op[addC]
- 將 B.mBackStackNesting 賦值給 C,完成 BackStack 中對 B 的替換
- B.mBackStackNesting減一 或者將 B.mBackStackNesting 置為 0, 使B在開啟新介面時得到釋放。
做完上述操作,才算真正的將 B destroy 掉而且保證堆疊的正確性。 除此,startFragmentAndDestroyCurrent
還提供了第二個引數, 這個引數是做什麼的呢?是用來控制轉場動畫的。假設 A->B 使用 startFragment
和轉場動畫 a,B->C 使用 startFragmentAndDestroyCurrent
和轉場動畫 b,那麼從 C 返回到 A 時,該使用轉場動畫 a 還是 轉場動畫 b 呢?這就取決於你第二個引數傳什麼了。