One of the most common challenges Android developers face is managing data across configuration changes, such as screen rotations. Traditionally, saving and restoring data required handling onSaveInstanceState()
or using retained fragments. The ViewModel
architecture component simplifies this process by surviving configuration changes and retaining UI-related data. But how does the ViewModel
achieve this persistence internally? Let’s dive deep into the mechanics and internal implementation of the ViewModel
system.
What is a ViewModel?
A ViewModel
is a lifecycle-aware component designed to hold and manage UI-related data in a way that survives configuration changes. Unlike an Activity
or Fragment
, which can be recreated during such changes, a ViewModel
is retained as long as its owning Lifecycle
(e.g., an Activity
or Fragment
) is alive.
How Does ViewModel Survive Configuration Changes?
The key to the ViewModel
‘s persistence lies in its relationship with the ViewModelStore
, which is managed by a ViewModelStoreOwner
(e.g., Activity
, Fragment
). Here’s an in-depth look at how this works:
1. The Lifecycle of an Activity
When an Activity
undergoes a configuration change, the Android system destroys the old instance and creates a new one. To retain certain objects (like the ViewModel
) across this recreation, the Android system provides the NonConfigurationInstance mechanism.
- The
ComponentActivity
class (a part ofandroidx.activity
) uses this mechanism to retain theViewModelStore
, which is a container forViewModel
instances. - Upon recreation, the new
Activity
instance retrieves the sameViewModelStore
, allowing it to access existingViewModel
instances.
2. The Role of ViewModelStore
The ViewModelStore
is a simple map-like structure that holds ViewModel
instances, keyed by their names. Here’s how it’s defined:
public class ViewModelStore { private final HashMap<String, ViewModel> mMap = new HashMap<>(); void put(String key, ViewModel viewModel) { mMap.put(key, viewModel); } ViewModel get(String key) { return mMap.get(key); } void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } }
. . .
3. Integration in ComponentActivity
The ComponentActivity
(a base class for activities in Jetpack) initializes and retains the ViewModelStore
. Here’s how it works internally:
a. Initialization
When an Activity
is created, its ViewModelStore
is initialized:
private ViewModelStore mViewModelStore; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } }
b. Retention Across Config Changes
When the Activity
is about to be destroyed due to a configuration change, the ViewModelStore
is retained through onRetainNonConfigurationInstance()
:
@Override protected Object onRetainNonConfigurationInstance() { return mViewModelStore; }
During recreation, the retained ViewModelStore
is restored:
@Override public Object getLastNonConfigurationInstance() { return mViewModelStore; }
This mechanism ensures the ViewModelStore
(and thus the ViewModel
instances) persist across configuration changes.
Job Offers
4. ViewModel Creation and Retrieval
The ViewModelProvider
is responsible for managing the lifecycle of ViewModel
instances. When you request a ViewModel
, the ViewModelProvider
checks if an instance already exists in the ViewModelStore
. If it does, the existing instance is returned; otherwise, a new instance is created.
a. Getting a ViewModel
public <T extends ViewModel> T get(Class<T> modelClass) { String key = modelClass.getCanonicalName(); ViewModel viewModel = mViewModelStore.get(key); if (viewModel == null) { viewModel = mFactory.create(modelClass); mViewModelStore.put(key, viewModel); } return (T) viewModel; }
- The
ViewModelProvider
uses the class name as the key to store and retrieveViewModel
instances. - If no instance exists for the given class, a new one is created using a
ViewModelProvider.Factory
.
b. Lifecycle Awareness
The ViewModel
is cleared automatically when the Activity
or Fragment
is truly destroyed (e.g., back navigation). The clear()
method in ViewModelStore
ensures that resources are released:
void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); }
Internal Flow of ViewModel Persistence
Activity Creation:
- The
ViewModelStore
is initialized.
Requesting a ViewModel:
- The
ViewModelProvider
checks theViewModelStore
for an existing instance. - If none exists, a new instance is created and stored.
Configuration Change:
- The
ViewModelStore
is retained viaonRetainNonConfigurationInstance()
. - The new
Activity
retrieves the retainedViewModelStore
.
Activity Destruction:
- If the
Activity
is truly destroyed (not a config change), theViewModelStore
and its contents are cleared.
Why Use ViewModel?
Advantages
- Persistence Across Config Changes: No need to manually save and restore data.
- Lifecycle Awareness: Automatically cleared when the
Activity
orFragment
is destroyed. - Separation of Concerns: Keeps UI logic out of
Activity
andFragment
classes.
Limitation
- The
ViewModel
is not a replacement foronSaveInstanceState()
for persisting small amounts of data across process death. - Data in a
ViewModel
is not saved when the app is completely killed by the system.
Conclusion
The ViewModel
architecture component is a powerful tool for managing UI-related data in Android. Its ability to survive configuration changes is made possible by the ViewModelStore
, which is retained across Activity
recreations using the onRetainNonConfigurationInstance()
mechanism. By understanding the internal workings of the ViewModel
, developers can better appreciate its role in simplifying state management and ensuring a seamless user experience.
Embrace the ViewModel
in your projects to reduce boilerplate code and achieve cleaner architecture!
This article is previously published on proandroiddev.com.