Issue
I'm currently trying the new Jetpack ViewModel with savedState.
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-rc01'
I'm using 1 Activity and trying to share 1 ViewModel with 2 Fragments but when I try to start the second fragment I get the error from the title.
This is how I'm calling the ViewModel with the savedInstance:
val repository = (activity?.application as App).getRepository()
viewModel = activity?.run {
ViewModelFactory(repository, this, savedInstanceState).create(MainViewModel::class.java)
} ?: throw Exception("Invalid Activity")
And this is my log:
java.lang.IllegalArgumentException: SavedStateProvider with the given key is already registered
at androidx.savedstate.SavedStateRegistry.registerSavedStateProvider(SavedStateRegistry.java:111)
at androidx.lifecycle.SavedStateHandleController.attachToLifecycle(SavedStateHandleController.java:50)
at androidx.lifecycle.SavedStateHandleController.create(SavedStateHandleController.java:70)
at androidx.lifecycle.AbstractSavedStateViewModelFactory.create(AbstractSavedStateViewModelFactory.java:67)
at androidx.lifecycle.AbstractSavedStateViewModelFactory.create(AbstractSavedStateViewModelFactory.java:84)
at com.xxx.yyy.presentation.details.DetailsFragment.onCreate(DetailsFragment.kt:29)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2586)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:838)
at androidx.fragment.app.FragmentTransition.addToFirstInLastOut(FragmentTransition.java:1197)
at androidx.fragment.app.FragmentTransition.calculateFragments(FragmentTransition.java:1080)
at androidx.fragment.app.FragmentTransition.startTransitions(FragmentTransition.java:119)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1866)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
at androidx.fragment.app.FragmentManagerImpl$2.run(FragmentManagerImpl.java:150)
Looks like it's trying to use a SavedState which was already registered and hence the error? I thought that was the whole point of the library. Can anyone help or point on how to use this passing arguments to the ViewModel and using the savedStateHandle?
Solution
You should never be calling create
yourself - by doing so, you're not actually using the retained ViewModel
that is already created, causing the AbstractSavedStateViewModelFactory
to attempt to register the same key more than once.
Instead, you should be passing your ViewModelFactory
to a ViewModelProvider
instance to retrieve the already existing ViewModel or creating it only if necessary:
val repository = (activity?.application as App).getRepository()
viewModel = activity?.run {
val factory = ViewModelFactory(repository, this, savedInstanceState)
ViewModelProvider(this, factory).get(MainViewModel::class.java)
} ?: throw Exception("Invalid Activity")
If you depend on fragment-ktx
of version 1.1.0
or higher, you can instead use the by activityViewModels
Kotlin property delegate, which lazily uses ViewModelProvider
under the hood:
val viewModel: MainViewModel by activityViewModels {
val repository = (activity?.application as App).getRepository()
ViewModelFactory(repository, requireActivity(), savedInstanceState)
}
Answered By - ianhanniballake
Answer Checked By - Marilyn (JavaFixing Volunteer)