Android : Deep dive into SavedStateHandle
SavedStateHandle: APIs, how it works with ViewModel and survive the Configuration changes.
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 theViewModel
class.It stores
key-value
pairs of data that need to be preserved across configuration changes.These
key-value
pairs are saved and retrieved bySavedStateHandle
during the lifecycle of aViewModel
.
When a key-value
pair is added or modified in the SavedStateHandle
internally updates the LinkedHashMap
accordingly. 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 fromMainThread
.<T>
means any of kind object that is supported.Before saving a
value
,set()
method callsvalidateValue(value)
to make sure thevalue
is listed in the Supported Objects List elsethrow 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 aViewModel
.SavedStateHandle
is primarily designed as an internal mechanism for managingstate
withinViewModel
instances. It serves as acontainer
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
orFragment
is created, a new instance of the associatedViewModel
is created or retrieved. This initialization typically occurs in theonCreate()
method of theActivity
orFragment
.SavedStateHandle Association: During
ViewModel
initialization, theSavedStateHandle
is associated with theViewModel
. This association is usually established through a dependency injection framework or via theViewModelProvider
.SavedStateHandle Creation: The
SavedStateHandle
is initialized with aBundle
, which represents the saved state of theViewModel
. ThisBundle
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
orFragment
but retains theViewModel
instance, including theSavedStateHandle
. TheBundle
associated with theSavedStateHandle
is preserved by the system.ViewModel Recreation: After the configuration change, a new instance of the
Activity
orFragment
is created, and theViewModel
is recreated. TheSavedStateHandle
is recreated as well, using the preservedBundle
from the previous instance.State Restoration: Once the
ViewModel
andSavedStateHandle
is recreated, the values stored in theSavedStateHandle
is automatically restored from the preservedBundle
. 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...