LocalViewModelStoreOwner提前释放所需Model致应用崩溃,如何解决?
解决后台恢复时ViewModel实例化崩溃问题
问题本质
后台长时间运行后,系统可能回收了原页面的ViewModelStoreOwner(比如Activity/Fragment被销毁重建),此时通过LocalViewModelStoreOwner.current拿到的是新的实例。你直接调用viewModel(viewModelStoreOwner)时,系统默认使用NewInstanceFactory尝试创建PrevViewModel,但该类没有无参构造,因此触发InstantiationException。
具体解决方案
1. 显式传入ViewModel的Factory,避免依赖无参构造
无论何时获取PrevViewModel,都要传入当初创建它时使用的Factory,确保系统能正确实例化,即使ViewModelStoreOwner重建也不会出错。
修改代码如下:
// 显式传入PrevViewModel的Factory val prevViewModel: PrevViewModel = viewModel( viewModelStoreOwner = viewModelStoreOwner, factory = PrevViewModel.provideFactory(/* 传入当初创建时所需的参数 */) ) val viewModel: ThisViewModel = viewModel( viewModelStoreOwner = viewModelStoreOwner, factory = ThisViewModel.provideFactory(prevViewModel) )
2. 提升ViewModel作用域,避免跨页面直接持有引用
不要直接通过LocalViewModelStoreOwner获取其他页面的ViewModel,改用更高层级的作用域来共享ViewModel,比如:
- 绑定到Activity的
ViewModelStoreOwner,确保ViewModel在Activity生命周期内保留 - 使用导航组件的
navGraphViewModels,将ViewModel作用域限定在整个导航图内
示例(导航组件场景):
// 获取前一页所在导航图的ViewModel,作用域覆盖整个导航图 val prevViewModel: PrevViewModel = navGraphViewModels(R.id.target_nav_graph) { PrevViewModel.provideFactory(/* 参数 */) }
3. 检查ViewModelStoreOwner的生命周期状态
在获取ViewModel前,先判断ViewModelStoreOwner是否处于活跃状态,避免在其已销毁或未启动时尝试实例化ViewModel:
val storeOwner = LocalViewModelStoreOwner.current ?: return if (storeOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { val prevViewModel = viewModel( viewModelStoreOwner = storeOwner, factory = PrevViewModel.provideFactory(/* 参数 */) ) // 后续业务逻辑 } else { // 处理异常场景,比如导航回前页或提示用户 }
4. 改用事件驱动的跨页面通信
如果只是需要前页ViewModel的回调或数据,不要直接持有ViewModel实例,改用SharedFlow或事件总线传递数据,降低组件间耦合度,也避免因ViewModel生命周期不一致导致的崩溃。
内容的提问来源于stack exchange,提问作者Bobrovsky
相关产品推荐
相关产品推荐

