Understanding Android ViewModel: State Preservation, onRetainNonConfigurationInstance, and Implementation Details
This article explains how Android ViewModel preserves UI state across configuration changes, the role of SavedStateHandle and onRetainNonConfigurationInstance, the internal mechanisms involving ComponentActivity and ViewModelStore, and provides practical guidance on using factories, CreationExtras, and Hilt for custom ViewModel implementations.
1. ViewModel Origin – Starting from State Saving
1.1 SavedState is not a ViewModel feature
In the "State Saving and SavedState" chapter we introduced the relationship between ViewModel and SavedState . The SavedStateHandle solves two problems for ViewModel : accessing component arguments and preserving state.
Looking at the source, SavedStateHandle is passed to the ViewModel constructor, which means a default ViewModel cannot access component arguments or save state.
The significance of ViewModel for state saving is that it can survive the recreation of an Activity caused by configuration changes.
1.2 The rarely‑used onRetainNonConfigurationInstance()
Android’s Activity class contains an almost unused API onRetainNonConfigurationInstance() . It is called only when a configuration change forces the activity to be destroyed.
Compared with onSaveInstanceState(Bundle) , the former supports any type, has no size limit, and stores the object directly in memory, while the latter is limited to primitive, Parcelable, or Serializable types and is limited to 1 MB because of Binder constraints.
onSaveInstanceState(Bundle) onRetainNonConfigurationInstance()Call timing
Before/after
onStop()When configuration changes
Supported types
Primitive, Parcelable, Serializable
Any type
Size limit
≈1 MB (Binder limit)
No limit
Implementation
Deserialize via Binder then store in memory
Store directly in memory
Because its call timing is very limited, most developers prefer onSaveInstanceState(Bundle) for all state‑saving needs, which makes onRetainNonConfigurationInstance() rarely used.
1.3 Should we discard or extend it?
If you understand Binder’s serialization overhead, you will see that large objects such as loaded bitmaps are inefficient to store via onSaveInstanceState . onRetainNonConfigurationInstance() therefore provides a space for caching large objects across configuration changes.
Google’s ViewModel is actually built on top of this API, offering the same “survive configuration change” capability without the boiler‑plate.
1.4 How ViewModel crosses the configuration‑change gap
ComponentActivity’s source shows a final method onRetainNonConfigurationInstance() that returns a NonConfigurationInstances object containing a ViewModelStore . The store holds all ViewModel instances.
When the activity is recreated, the stored ViewModelStore is retrieved, giving back the same ViewModel objects.
Thus the secret of ViewModel’s persistence is the ViewModelStore saved in the non‑configuration instance.
2. What is ViewModel?
2.1 Definition
ViewModel is a container for UI‑level state and business logic. It caches state so that it survives configuration changes such as screen rotation, allowing the UI to reuse data without re‑fetching.
2.2 Simple usage
After adding a ViewModel, member variables that previously lived in an Activity are moved into the ViewModel, reducing UI‑logic coupling and making the UI easier to test and migrate.
State container
Avoid loss on configuration updates
2.3 Using SavedState
The previous chapter covers detailed usage of SavedState; this article does not repeat it.
3. ViewModel Analysis
3.1 Core components
3.1.1 ViewModel
The core ViewModel class only holds a tag map and a closeable set, which are cleared when the ViewModel is destroyed.
It is destroyed only when the component is killed for reasons other than a configuration change.
3.1.2 ViewModelStore & ViewModelStoreOwner
ViewModelStore is a map‑like container that stores ViewModel instances by key. ViewModelStoreOwner is an interface that provides access to a ViewModelStore , similar to LifecycleOwner .
The store must survive configuration changes; if its owner no longer needs it, clear() should be called.
3.1.3 ViewModelProvider & Factory
ViewModelProvider creates ViewModel instances using a Factory. Its get() method returns a cached instance or creates a new one via the factory.
3.1.4 Factory and CreationExtras
Older factories required a concrete constructor for each ViewModel, leading to many factory classes. The new CreationExtras mechanism works like an Intent‑style key‑value bundle, allowing a stateless factory to obtain any required parameters.
Pre‑defined keys include:
CreationExtras.Key
Descriptions
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY
Distinguishes multiple VM instances
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
Provides the Application context
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY
Owner needed to create a SavedStateHandle
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY
Owner needed to create a SavedStateHandle
SavedStateHandleSupport.DEFAULT_ARGS_KEY
Bundle used to create a SavedStateHandle
3.1.5 Small summary
The diagram below shows the relationship among all components.
3.2 ViewModel recap
3.2.1 Usage flow
When ComponentActivity reaches onCreate() , a ViewModelProvider is used to obtain a ViewModel via the default factory defaultViewModelProviderFactory , which is actually a SavedStateViewModelFactory .
3.2.2 ViewModel by‑delegate
Most developers now create ViewModels with the Kotlin by viewModels delegate, which internally uses the default factory and default CreationExtras unless overridden.
4. ViewModel in Real Projects
4.1 ViewModel with empty constructor
Simply use the delegate; no extra code is needed.
4.2 ViewModel that needs SavedStateHandle
ComponentActivity and Fragment already provide a SavedStateHandle, so the delegate works out‑of‑the‑box.
4.3 ViewModel that requires additional parameters
4.3.1 Custom factory + custom CreationExtras
Define a custom key, wrap the original CreationExtras with MutableCreationExtras , and retrieve the custom parameter inside the factory.
4.3.2 Using Hilt for dependency injection
Hilt can generate the factory and provide parameters automatically, removing the need for manual factory implementations.
Reference: Using Hilt for dependency injection
Reference: Using Hilt with other Jetpack libraries
5. Summary
ViewModel is the cornerstone of Android’s MVVM era. It acts as a state container that survives configuration changes, but many developers only use it superficially. Understanding the underlying mechanisms— onRetainNonConfigurationInstance , ViewModelStore , factories, and CreationExtras—enables more robust and flexible implementations.
The article covered the core concepts, component relationships, and several practical customization patterns, leaving room for deeper exploration.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.