fragment狀態(tài)保存問題
在使用jetpack的navigation組件過程中遇到的一個(gè)問題就是它內(nèi)部使用replace方式切換的fragment,這樣會(huì)導(dǎo)致fragment生命周期重走。這樣就不會(huì)保留之前的頁面狀態(tài)了,這就有點(diǎn)不友好了。查了一下大家使用的解決方案,主要有兩種
使用hide/show方式取代replace方式
繼續(xù)使用replace方式,想辦法保存頁面狀態(tài)
經(jīng)過對(duì)比這兩種方案發(fā)現(xiàn),navigation原生方式更合理。因?yàn)閔ide/show方案對(duì)內(nèi)存不友好的弊端很難消除,且項(xiàng)目越大,問題越明顯。
使用原生方式,就面臨了另一個(gè)問題,如何保存頁面狀態(tài)?Navigation設(shè)計(jì)初衷就是UI與數(shù)據(jù)分離,所以這個(gè)問題,拆解成兩個(gè)問題分別解決:
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ù),這里引出兩個(gè)問題
- 1.viewmodel的生命周期跟隨誰
跟隨fragment,那么fragment銷毀的時(shí)候,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)建的時(shí),重新注冊(cè)u(píng)iStatus的observer,注冊(cè)的時(shí)候,livedata會(huì)把上次數(shù)據(jù)重新發(fā)送一遍。
解決方法也很簡(jiǎn)單,只需要在基類的fragment中清除一下狀態(tài)就好了
還有一個(gè)問題,如果跳轉(zhuǎn)fragment的時(shí)候,攜帶了參數(shù),重新回到這個(gè)fragment的時(shí)候也會(huì)重新獲取到。這個(gè)也是需要清除的
綜合以上各種問題的解決方案,BaseFragment應(yīng)當(dā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()
}