Why it is recommend to use the ViewModel architecture component if it does not handle process death?
For example, if I save state with onSaveInstanceState, the state persists both for configuration change and process death while ViewModel only survives configuration change. To survive process death, I need to get the state from the ViewModel and use the same onSaveInstanceState mechanism.
What am I missing?
Why it is recommend to use the ViewModel architecture component if it does not handle process death?
What am I missing?
You were right until January 2020 when, finally, Google released androidx.lifecycle:lifecycle-viewmodel-savedstate and then made the AndroidX Activity, Fragment and Navigation latest library versions provide SavedStateViewModelFactory as the default ViewModelProvider.Factory.
Now you can provide a SavedStateHandle into your ViewModel which can help with persisting/restoring stuff across process death.
class MyViewModel(private val savedStateHandle: SavedStateHandle): ViewModel() {
val someState: MutableLiveData<String> = savedStateHandle.getLiveData("someState")
}
or
class MyAndroidViewModel(application: Application, private val savedStateHandle: SavedStateHandle): AndroidViewModel(application) {
val someState: MutableLiveData<String> = savedStateHandle.getLiveData("someState")
}
And these would just work via their default constructors as SavedStateViewModelFactory
creates them via reflection.
If you need a SavedStateHandle
and also additional parameters, then instead of using a default ViewModelProvider.Factory
, you can instead extend from AbstractSavedStateViewModelFactory
that will give you a SavedStateHandle
that actually works and persists/restores across process death automatically.
val viewModel = ViewModelProvider(this, myAbstractSavedStateViewModelFactoryImpl).get(MyViewModel::class.java)
Or with KTX things
private val viewModel by viewModels { myAbstractSavedStateViewModelFactoryImpl }
You can also scope ViewModels to a Jetpack Navigation's NavGraph, that's a bit trickier but doable:
inline fun <reified T : ViewModel> SavedStateRegistryOwner.createAbstractSavedStateViewModelFactory(
arguments: Bundle,
crossinline creator: (SavedStateHandle) -> T
): ViewModelProvider.Factory {
return object : AbstractSavedStateViewModelFactory(this, arguments) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(
key: String, modelClass: Class<T>, handle: SavedStateHandle
): T = creator(handle) as T
}
}
inline fun <reified T : ViewModel> Fragment.navGraphSavedStateViewModels(
@IdRes navGraphId: Int,
crossinline creator: (SavedStateHandle) -> T
): Lazy<T> {
// Wrapped in lazy to not search the NavController each time we want the backStackEntry
val backStackEntry by lazy { findNavController().getBackStackEntry(navGraphId) }
return createViewModelLazy(T::class, storeProducer = {
backStackEntry.viewModelStore
}, factoryProducer = {
backStackEntry.createAbstractSavedStateViewModelFactory(
arguments = backStackEntry.arguments ?: Bundle(), creator = creator
)
})
}
Then
class MyFragment: Fragment() {
private val mySharedViewModel by navGraphSavedStateViewModels(R.id.registration_graph) { handle ->
MySharedViewModel(handle)
}
}