Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SavedStateProvider with the given key is already registered

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?

like image 203
davisjp Avatar asked Nov 09 '19 01:11

davisjp


1 Answers

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)
}
like image 192
ianhanniballake Avatar answered Sep 28 '22 08:09

ianhanniballake