使用Jetpack ViewModel SavedState跨Fragment共享时注册冲突报错
这个错误的核心原因是你在每个Fragment里都用自己的savedInstanceState去构建ViewModel工厂,导致两个Fragment尝试给同一个ViewModel注册独立的SavedStateProvider,自然就冲突了。共享带SavedState的ViewModel时,必须把它和Activity的SavedState生命周期绑定,而不是Fragment的。
问题根源拆解
当你在Fragment中传入自己的savedInstanceState到ViewModelFactory时,工厂会基于Fragment的SavedStateRegistry来创建ViewModel的SavedStateHandle。但两个Fragment共享同一个ViewModel时,第二次创建就会发现同一个key的Provider已经被第一个Fragment注册过了,所以抛出SavedStateProvider with the given key is already registered异常。
正确的实现步骤
1. 修正ViewModelFactory的实现
你的Factory应该继承AbstractSavedStateViewModelFactory,并接收Activity(作为SavedStateRegistryOwner),而不是Fragment的savedInstanceState:
class ViewModelFactory( private val repository: YourRepositoryType, owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) { override fun <T : ViewModel> create( key: String, modelClass: Class<T>, handle: SavedStateHandle ): T { if (modelClass.isAssignableFrom(MainViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return MainViewModel(repository, handle) as T } throw IllegalArgumentException("Unknown ViewModel class") } }
2. 在Fragment中正确获取共享ViewModel
两个Fragment都要通过Activity作为ViewModelProvider的Owner来获取ViewModel,这样ViewModel会绑定到Activity的生命周期和SavedStateRegistry:
val repository = (requireActivity().application as App).getRepository() val factory = ViewModelFactory(repository, requireActivity()) viewModel = ViewModelProvider(requireActivity(), factory)[MainViewModel::class.java]
为什么这样能解决问题?
- 用
requireActivity()作为SavedStateRegistryOwner,ViewModel的SavedStateHandle会和Activity的SavedState绑定,不管哪个Fragment获取这个ViewModel,都会共用同一个SavedStateProvider,不会重复注册。 - 当Activity因配置变化重建时,ViewModel的状态会通过Activity的SavedState自动恢复,两个Fragment也能拿到最新的状态。
额外注意事项
- 不要在Fragment中传入自己的
savedInstanceState给Factory,除非这个ViewModel是Fragment独有的,不与其他组件共享。 - 确保你的
MainViewModel在构造函数中接收SavedStateHandle,用来保存和恢复状态:
class MainViewModel( private val repository: YourRepositoryType, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // 用savedStateHandle存取状态,比如: val someState = savedStateHandle.getLiveData<String>("KEY_SOME_STATE") }
内容的提问来源于stack exchange,提问作者davisjp

