Search code examples
androidandroid-viewmodel

ViewModel and process death


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?


Solution

  • 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)
        }
    }