自定义ViewModel Factory每次返回新实例而非已有实例如何解决
问题原因分析
- ViewModel 实例的存储和复用和你传入
ViewModelProvider的第一个参数ViewModelStoreOwner直接绑定,每个独立的ViewModelStoreOwner都有专属的ViewModel缓存空间。你当前传入的是Fragment实例自身,如果每次展示Fragment时系统都会创建全新的Fragment实例(比如Navigation组件重复导航到同一目的地、手动调用事务remove后重新add Fragment),新的Fragment实例的缓存空间是空的,自然会触发Factory创建新的ViewModel实例。 - 如果你是在配置变更(转屏、系统主题切换)的场景下出现该问题,说明你获取ViewModel的代码执行时机错误:如果逻辑放在了异步回调、
onViewCreated之后的晚执行逻辑,或者你在Fragment中手动调用过clearViewModelStore之类的清理逻辑,也会导致缓存丢失触发新建。 - 你提供的Factory代码存在笔误隐患:你声明的Application类型属性名为
app,但创建ViewModel时传入的是application,如果实际运行时这个变量指向的不是Factory持有的Application实例,可能引发额外的异常导致缓存失效。
修复方案
场景1:仅需要在配置变更场景下复用ViewModel
将获取ViewModel的逻辑移动到Fragment的onCreate或者onCreateView的最顶部执行,同时修正Factory的笔误即可,修正后的代码如下:
// 修正后的自定义Factory class MyViewModelFactory( private val app: Application, private val someClass: SomeClass, ) : ViewModelProvider.Factory { @Suppress("unchecked_cast") override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(MyViewModel::class.java)) { // 替换为实际持有的app属性 return MyViewModel(app, someClass) as T } throw IllegalArgumentException("Unknown ViewModel class") } } // 获取ViewModel的代码放在onCreate/onCreateView顶部 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProvider(this, MyViewModelFactory(requireActivity().application, someClass)).get( MyViewModel::class.java ) }
场景2:需要在多次创建的Fragment实例间复用ViewModel
修改ViewModelProvider的第一个参数,绑定到生命周期更长的ViewModelStoreOwner即可:
- 如果需要在当前导航图范围内复用:传入
findNavController().getBackStackEntry(R.id.你的导航图ID).viewModelStoreOwner - 如果需要在整个Activity生命周期内复用:直接传入
requireActivity()
示例代码如下:
// Activity级复用的示例 viewModel = ViewModelProvider(requireActivity(), MyViewModelFactory(requireActivity().application, someClass)).get( MyViewModel::class.java )
内容的提问来源于stack exchange,提问作者Pavel Poley
相关产品推荐
相关产品推荐

