Saving UI State for Android (ViewModels with Saved State)
Tags: android, kotlin, java, viewmodels
Published: 2022-07-12 07:52:42



ViewModel has developed into one of the most "core" Android Jetpack libraries since its debut. Approximately 40% of Android developers have included ViewModels in their apps, according to our 2019 Developer Benchmarking data. This might not be obvious if you're unfamiliar with viewmodels: By separating your data from your UI, viewmodels encourage better architecture. This makes it simple to manage UI lifecycles and increases testability. Visit ViewModels for a detailed explanation. 

Since ViewModel objects, not activities, are typically where UI state is kept or referenced, using onSaveInstanceState() needs some boilerplate that the saved state module can take care of for you.

ViewModel objects get a SavedStateHandle object through its constructor if this module is being used. You can write and retrieve items to and from the preserved state using this object, which is a key-value map. These values are still accessible through the same object even after the system kills the process.

### The Challenge of onSaveInstanceState

There was a perplexing problem with onSaveInstanceState when ViewModels first came out. There are three techniques to destroy activities and fragments:

You intended to navigate away permanently: The user navigates away or intentionally quits the activity, such as by clicking the back button or activating a code that calls finish (). The action has ended completely.
There is a configuration change: The user turns the device on or makes another configuration modification. The activity needs to be restored right away.

The app is put in the background and its process is terminated: This occurs when the device has to free up memory quickly because it is running low on memory. The activity will need to be rebuilt when the user returns to your app.

You want to restore the activity in cases 2 and 3. Because the ViewModel is not destroyed upon a configuration change, ViewModels have always assisted you in handling case 2. However, in situation 3, the ViewModel is also destroyed, necessitating the use of the onSaveInstanceState callback in your activity to save and restore data.

### Saved state module for ViewModel

A new library by the name of ViewModel SavedState has been made available by Google as an extension to ViewModel to provide Activity or Fragment state persistence through process stop.
The ViewModel Saved State module handles the third scenario (process death). There is no longer a requirement for the ViewModel to communicate state to and from the activity. Instead, you can now manage to save and restore data entirely within the ViewModel. The ViewModel can now manage and store all of its data effectively.

A SavedStateHandle is used for this; it's a key-value map of data and is quite similar to a bundle. The ViewModel contains this SavedStateHandle "bundle," which endures the termination of background processes. You can now save any information you previously had to save in onSaveInstanceState in the SavedStateHandle. For example, you might keep information in the SavedStateHandle, such as a user's ID.

Note that the code below is snippets from this CodeLab reWritten in Kotlin, though.

### Setting Up

#### Step 1: Add the Dependency

SavedStateHandle is a distinct library presently in alpha (meaning the API may change, and we're seeking feedback). The additional dependence is:

*app/build.gradle:*

```groovy
dependencies {
	


	 def version = "2.2.0"


	 implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$version"


	 implementation "androidx.lifecycle:lifecycle-livedata-ktx:$version"


	 implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$version"


	 // or lifeycle-compiler if not using Java 8


	 //kapt "androidx.lifecycle:lifecycle-compiler:$version"


	 implementation "androidx.activity:activity-ktx:1.1.0"


	 implementation "androidx.fragment:fragment-ktx:1.2.1"


	}

```
*SavedState_DetailsViewModel:*

```kotlin
class DetailViewModel(
	


	 private val handle: SavedStateHandle


	) : ViewModel() {


	 fun loadData() {


	 val id = handle["id"] ?: "default"


	 // Load data for ID...


	 }


	}
```

### Step 2: Update the call to ViewModelProvider

The following step is to develop a type of ViewModel with a SavedStateHandle. Update your call to ViewModelProvider in your activity's or fragment's onCreate method to:



	


```kotlin
	// This ktx requires at least androidx.fragment:fragment-ktx:1.1.0 or


	val viewModel by viewModels { SavedStateVMFactory(this) }


	// Or the non-ktx way...


	val viewModel = ViewModelProvider(this, SavedStateVMFactory(this))


	 .get(MyViewModel::class.java)
```

The class that creates ViewModels is called a ViewModel factory, and another ViewModel factory named SavedStateVMFactory produces ViewModels with SavedStateHandles. The activity/fragment supplied in will now be connected by a SavedStateHandle to the newly generated ViewModel.

### Step 3: Use SaveStateHandle in ViewModel

You may then utilize the SavedStateHandle in your ViewModel after completing this. Here is an illustration of how to store a user ID in the SavedStateHandle:

```kotlin

class MyViewModel(state : SavedStateHandle) : ViewModel() {

    // Keep the key as a constant
    companion object {
        private val USER_KEY = "userId"
    }
    
    private val savedStateHandle = state
    
    fun saveCurrentUser(userId: String) {
        // Sets a new value for the object associated to the key.
        savedStateHandle.set(USER_KEY, userId)
    }
    
    fun getCurrentUser(): String {
        // Gets the current value of the user id from the saved state handle
        return savedStateHandle.get(USER_KEY)?: ""
    }
}
```

**Construct**: MyViewModel accepts SavedStateHandle as a constructor parameter.
**Save**: An illustration of saving data in a SavedStateHandle is provided by the save NewUser function. You first save the current userId and then the key-value pair USER KEY. The SavedStateHandle should be updated as data changes in the ViewModel.
**Retrieve**: The current value saved in the SaveStateHandle can be obtained using the method savedStateHandle.get(USER KEY).
 Now, if the activity is destroyed, we can be ensured the SavedStateHandle will have our data.