fragment狀態(tài)保存問題
在使用jetpack的navigation組件過程中遇到的一個問題就是它內(nèi)部使用replace方式切換的fragment胚股,這樣會導致fragment生命周期重走土榴。這樣就不會保留之前的頁面狀態(tài)了,這就有點不友好了恶守。查了一下大家使用的解決方案甘畅,主要有兩種
使用hide/show方式取代replace方式
繼續(xù)使用replace方式,想辦法保存頁面狀態(tài)
經(jīng)過對比這兩種方案發(fā)現(xiàn)感混,navigation原生方式更合理竹伸。因為hide/show方案對內(nèi)存不友好的弊端很難消除泥栖,且項目越大,問題越明顯勋篓。
使用原生方式聊倔,就面臨了另一個問題,如何保存頁面狀態(tài)生巡?Navigation設(shè)計初衷就是UI與數(shù)據(jù)分離耙蔑,所以這個問題,拆解成兩個問題分別解決:
1.頁面問題
Google官方有推薦方案孤荣,就是保存view甸陌。
abstract class BaseFragment : Fragment() {
private var rootView : View ?= null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (rootView == null){
rootView = inflater.inflate(getLayoutId(), null)
}
return rootView
}
abstract fun getLayoutId():Int
}
2.數(shù)據(jù)問題
通過viewmodel保存數(shù)據(jù),這里引出兩個問題
- 1.viewmodel的生命周期跟隨誰
跟隨fragment盐股,那么fragment銷毀的時候钱豁,viewmodel就沒有了。跟隨activity疯汁,那么activcity不銷毀的情況下牲尺,viewmodel就一直存在
看源碼發(fā)現(xiàn),Navigation切換fragment用的是childFragmentManager幌蚊,所有的fragment的父fragment都是NavHostFragment谤碳,于是跟隨requireParentFragment()就可以了
override fun initVM(): StickerViewModel = ViewModelProvider(requireParentFragment())[StickerViewModel::class.java]
- 2.fragment重新創(chuàng)建的時,重新注冊uiStatus的observer溢豆,注冊的時候蜒简,livedata會把上次數(shù)據(jù)重新發(fā)送一遍。
解決方法也很簡單漩仙,只需要在基類的fragment中清除一下狀態(tài)就好了
還有一個問題搓茬,如果跳轉(zhuǎn)fragment的時候,攜帶了參數(shù)队他,重新回到這個fragment的時候也會重新獲取到。這個也是需要清除的
綜合以上各種問題的解決方案麸折,BaseFragment應(yīng)當如下
abstract class BaseVMFragment<VM : BaseViewModel> : Fragment() {
protected var mContentView: View? = null
protected lateinit var mViewModel: VM
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
if (mContentView == null) {
mContentView = inflater.inflate(getLayoutResId(), container, false)
}
return mContentView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mViewModel = initVM()
if (isNeedClearState()) {
mViewModel.clearUIState()
}
initView()
initData()
startObserve()
super.onViewCreated(view, savedInstanceState)
}
override fun onDestroyView() {
//需要清除參數(shù)和uiState
arguments?.clear()
super.onDestroyView()
}
open fun isNeedClearState(): Boolean {
return true
}
abstract fun getLayoutResId(): Int
abstract fun initVM(): VM
abstract fun initView()
abstract fun initData()
abstract fun startObserve()
}