I need to use an AndroidViewModel
with application context and a SavedStateHandle
. I have it already working with application context, but I fail adding a SavedStateHandle
to it.
This is what I have, with only the application context:
// A1. get ViewModel in Fragment
val viewModel = ViewModelProvider(viewLifecycleOwner).get(MyViewModel::class.java)
// A2. MyViewModel derives from my custom BaseAndroidViewModel
class MyViewModel(application: Application) :BaseAndroidViewModel(application)
// A3. BaseAndroidViewModel in turn derives from AndroidViewModel
open class BaseAndroidViewModel(application: Application) : AndroidViewModel(application)
I assume for this question this could likely be reduced to:
// B1. get ViewModel in Fragment
val viewModel = ViewModelProvider(viewLifecycleOwner).get(MyViewModel::class.java)
// B2. BaseAndroidViewModel in turn derives from AndroidViewModel
class MyViewModel(application: Application) : AndroidViewModel(application)
So, for also having a SavedStateHandle
in MyViewModel
, how would I have to modify the call in the fragment (line B1
in the example code) ? Do I need an explicit call to the factory SavedStateViewModelFactory
? How exactly would that look like? (I am still new to Kotlin/Android, I've never worked with a factory before)
The purpose of the ViewModel is to acquire and keep the information that is necessary for an Activity or a Fragment. The Activity or the Fragment should be able to observe changes in the ViewModel. ViewModels usually expose this information via LiveData or Android Data Binding.
UI state is usually stored or referenced in ViewModel objects and not activities, so using onSaveInstanceState() requires some boilerplate that the saved state module can handle for you. When using this module, ViewModel objects receive a SavedStateHandle object through its constructor.
ViewModels are automatically destroyed by the system when your user backs out of your activity or fragment or if you call finish() , which means the state will be cleared as the user expects in these scenarios. Unlike saved instance state, ViewModels are destroyed during a system-initiated process death.
AndroidViewModel inherits ViewModel, so it has all the same functionality. The only added functionality for AndroidViewModel is that it is context aware: when initializing AndroidViewModel you have to pass the context as a parameter. This can be used if you want to show toasts for example.
EDIT: in the final release of AndroidX-Activity 1.2.0
and AndroidX-Fragment 1.1.0
, they made SavedStateViewModelFactory
the default factory in AppCompatActivity/Fragment, so it is not needed to override the default factory (which is what the second half of this answer does.)
Updating and then using
class MyViewModel(val savedStateHandle: SavedStateHandle): ViewModel()
class MyAndroidViewModel(application: Application, val savedStateHandle: SavedStateHandle): AndroidViewModel(application)
Should just work.
ORIGINAL:
how would I have to modify the call in the fragment (line B1 in the example code) ? Do I need an explicit call to the factory SavedStateViewModelFactory? How exactly would that look like?
In AndroidX-Activity 1.2.0, they added a new method called getDefaultViewModelProviderFactory()
:
+ @NonNull + @Override + public ViewModelProvider.Factory getDefaultViewModelProviderFactory() { + if (getApplication() == null) { + throw new IllegalStateException("Your activity is not yet attached to the " + + "Application instance. You can't request ViewModel before onCreate call."); + } + return ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()); + } +
Which means if I have a BaseActivity
or a BaseFragment
, I can swap this out for the SavedStateViewModelFactory
from viewmodel-savedstate
:
class BaseActivity: AppCompatActivity() {
override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory =
SavedStateViewModelFactory(application, this, intent?.extras ?: Bundle())
}
class BaseFragment: Fragment() {
override fun getDefaultViewModelProviderFactory(): ViewModelProvider.Factory =
SavedStateViewModelFactory(requireActivity().application, this, arguments ?: Bundle())
}
Once you have that, you can rely on the automatic instantiation of ViewModel with SavedStateHandle
as one of their arguments:
class MyViewModel(val savedStateHandle: SavedStateHandle): ViewModel()
class MyAndroidViewModel(application: Application, val savedStateHandle: SavedStateHandle): AndroidViewModel(application)
Beware that the order application, savedStateHandle
is expected by SavedStateViewModelFactory
.
However, if you do need custom arguments on top of that, then you'd have to provide a object: AbstractSavedStateViewModelFactory
when you invoke the ViewModelProvider(viewModelStoreOwner).get(...)
method
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With