使用Nav-graph作用域ViewModel与AbstractSavedStateViewModelFactory时出现SavedStateProvider重复注册异常求助
我之前也碰到过类似的情况,你这个崩溃的核心原因其实是错误地把Activity作为SavedStateRegistryOwner传给了AbstractSavedStateViewModelFactory,和常见的重复注册场景本质不同,下面给你拆解原因和修复步骤:
问题根源
导航图作用域的ViewModel,它的生命周期是和对应的NavBackStackEntry(也就是导航图的后台栈条目)绑定的,而不是Activity。当你第一次进入FirstPageFragment时,你的Factory把SavedStateProvider注册到了Activity的SavedStateRegistry里;当你返回StartFragment,导航图的NavBackStackEntry会被销毁,但Activity的SavedStateRegistry中注册的Provider并不会随之移除;等你再次进入FirstPageFragment时,新的Factory又尝试注册同一个key的Provider,直接触发了「给定key的SavedStateProvider已注册」的异常。
修复步骤
1. 调整ViewModel Factory的构造逻辑
首先修改MyViewModel.Factory,不再依赖Activity,而是直接接收SavedStateRegistryOwner和Application(避免不必要的Activity引用):
class MyViewModel(application: Application, savedStateHandle: SavedStateHandle, val someString: String) : AndroidViewModel(application){ class Factory( private val owner: SavedStateRegistryOwner, private val application: Application, private val someString: String ) : AbstractSavedStateViewModelFactory(owner, null) { override fun <T : ViewModel?> create( key: String, modelClass: Class<T>, handle: SavedStateHandle ): T { if (modelClass.isAssignableFrom(MyViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return MyViewModel(application, handle, someString) as T } throw IllegalArgumentException("Unable to construct viewmodel") } } // ... 其他ViewModel逻辑 }
2. 传入正确的SavedStateRegistryOwner到Factory
在FirstPageFragment中,navGraphViewModels的lambda参数会提供当前导航图对应的ViewModelStoreOwner,而这个Owner其实就是NavBackStackEntry(同时也是SavedStateRegistryOwner),我们直接用它来创建Factory:
class FirstPageFragment: Fragment() { private val myViewModel: MyViewModel by navGraphViewModels(R.id.nav_mission) { owner -> // owner 是导航图对应的NavBackStackEntry,它才是当前ViewModel的正确SavedStateRegistryOwner MyViewModel.Factory(owner, requireActivity().application, "hello world1") } // ... 其他Fragment逻辑 }
为什么这样能解决问题?
修改后,SavedStateProvider会被注册到导航图的NavBackStackEntry的SavedStateRegistry中,当你返回StartFragment导致导航图的BackStackEntry销毁时,对应的Provider也会被自动移除。再次进入导航图时,新的BackStackEntry会创建新的Provider,自然不会出现重复注册的冲突了。
内容的提问来源于stack exchange,提问作者BabyishTank

