fragment状态保存问题
在使用jetpack的navigation组件过程中遇到的一个问题就是它内部使用replace方式切换的fragment,这样会导致fragment生命周期重走。这样就不会保留之前的页面状态了,这就有点不友好了。查了一下大家使用的解决方案,主要有两种
使用hide/show方式取代replace方式
继续使用replace方式,想办法保存页面状态
经过对比这两种方案发现,navigation原生方式更合理。因为hide/show方案对内存不友好的弊端很难消除,且项目越大,问题越明显。
使用原生方式,就面临了另一个问题,如何保存页面状态?Navigation设计初衷就是UI与数据分离,所以这个问题,拆解成两个问题分别解决:
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.数据问题
通过viewmodel保存数据,这里引出两个问题
- 1.viewmodel的生命周期跟随谁
跟随fragment,那么fragment销毁的时候,viewmodel就没有了。跟随activity,那么activcity不销毁的情况下,viewmodel就一直存在
看源码发现,Navigation切换fragment用的是childFragmentManager,所有的fragment的父fragment都是NavHostFragment,于是跟随requireParentFragment()就可以了
override fun initVM(): StickerViewModel = ViewModelProvider(requireParentFragment())[StickerViewModel::class.java]
- 2.fragment重新创建的时,重新注册uiStatus的observer,注册的时候,livedata会把上次数据重新发送一遍。
解决方法也很简单,只需要在基类的fragment中清除一下状态就好了
还有一个问题,如果跳转fragment的时候,携带了参数,重新回到这个fragment的时候也会重新获取到。这个也是需要清除的
综合以上各种问题的解决方案,BaseFragment应当如下
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() {
//需要清除参数和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()
}