Android : Deep dive into SavedStateHandle

Android : Deep dive into SavedStateHandle

SavedStateHandle: APIs, how it works with ViewModel and survive the Configuration changes.

ยท

4 min read

Basic

SavedStateHandle, introduced in the AndroidX library, provides a convenient way to persist and restore data during configuration changes in Android applications. Under the hood, SavedStateHandle leverages several key components and mechanisms to achieve this functionality. We will explore the internal implementation of the Android SavedStateHandle and understand how it works.

  • SavedStateHandle is part of the Android Architecture Components and is tightly integrated with the ViewModel class.

  • It stores key-value pairs of data that need to be preserved across configuration changes.

  • These key-value pairs are saved and retrieved by SavedStateHandle during the lifecycle of a ViewModel.

When a key-value pair is added or modified in the SavedStateHandle internally updates the LinkedHashMapaccordingly. Similarly, when retrieving a value, SavedStateHandle looks up the corresponding key in the LinkedHashMap and returns the value if present.


Public APIs

// To set a value
fun <T> set(key: String, value: T?)

// To get a value
fun <T> get(key: String): T? 

// To remove a value
fun <T> remove(key: String): T? 

// To find a key present or not
fun contains(key: String): Boolean 

// To get a value as LiveData
fun <T> getLiveData(key: String): MutableLiveData<T>

// To get a value as LiveData with a initial value if has null value
fun <T> getLiveData(key: String, initialValue: T): MutableLiveData<T>

// To get a value as StateFlow with a initial value
fun <T> getStateFlow(key: String, initialValue: T): StateFlow<T>

Notes

  • All public APIs must be called from MainThread.

  • <T> means any of kind object that is supported.

  • Before saving a value, set() method calls validateValue(value) to make sure the value is listed in the Supported Objects List else throw IllegalArgumentException

  • Supported objects are:

      Boolean::class.javaPrimitiveType, BooleanArray::class.java,
      Double::class.javaPrimitiveType, DoubleArray::class.java,
      Int::class.javaPrimitiveType, IntArray::class.java,
      Long::class.javaPrimitiveType, LongArray::class.java,
      String::class.java, Array<String>::class.java,
      Binder::class.java, Bundle::class.java,
      Byte::class.javaPrimitiveType, ByteArray::class.java,
      Char::class.javaPrimitiveType, CharArray::class.java,
      CharSequence::class.java, Array<CharSequence>::class.java,
      ArrayList::class.java, Float::class.javaPrimitiveType,
      FloatArray::class.java, Parcelable::class.java,
      Array<Parcelable>::class.java, Serializable::class.java,
      Short::class.javaPrimitiveType, ShortArray::class.java,
      SparseArray::class.java, Size::class.java
      Int::class.javaPrimitiveType, SizeF::class.java
    

How does it survive the Configuration changes?

  • SavedStateHandle is associated with a ViewModel. SavedStateHandle is primarily designed as an internal mechanism for managing state within ViewModel instances. It serves as a container for persisting and restoring data across configuration changes.

SavedStateHandle survives configuration changes in Android by leveraging the ViewModel's lifecycle and the underlying architecture of the Android framework. When a configuration change occurs, such as a screen rotation, the system destroys and recreates the associated Activity or Fragment. However, the ViewModel, including its SavedStateHandle remains unaffected by this process.

  • ViewModel Initialization: When an Activity or Fragment is created, a new instance of the associated ViewModel is created or retrieved. This initialization typically occurs in the onCreate() method of the Activity or Fragment.

  • SavedStateHandle Association: During ViewModel initialization, the SavedStateHandle is associated with the ViewModel. This association is usually established through a dependency injection framework or via the ViewModelProvider.

  • SavedStateHandle Creation: The SavedStateHandle is initialized with a Bundle, which represents the saved state of the ViewModel. This Bundle is automatically provided by the framework and represents the data that needs to be persisted across configuration changes.

  • Bundle Preservation: When a configuration change occurs, the system destroys the Activity or Fragment but retains the ViewModel instance, including the SavedStateHandle. The Bundle associated with the SavedStateHandle is preserved by the system.

  • ViewModel Recreation: After the configuration change, a new instance of the Activity or Fragment is created, and the ViewModel is recreated. The SavedStateHandle is recreated as well, using the preserved Bundle from the previous instance.

  • State Restoration: Once the ViewModel and SavedStateHandle is recreated, the values stored in the SavedStateHandle is automatically restored from the preserved Bundle. This ensures that the state is maintained across the configuration change.


In a nutshell

*_* SavedStateHandle is a container that holds supported objects according to unique keys by leveraging LinkedHashMap. Simple key-value concept but very powerful.

*_* ViewModel instance provided by ViewModelProvider which is associated with an Activity.

*_* Every instance of a ViewModel contains an instance of SavedStateHandle.

*_* As ViewModel is not tied to Activity lifecycle, when there are configuration changes, ViewModelProvider restores the instance with the associated Activity including SavedStateHandle state.


That's it for today. Happy coding...

ย