Android SDK

How Android ViewModel works under the hood to survive to configuration change

Learn how a ViewModel instance survives to an Activity re-creation

Torcheux Frédéric
ProAndroidDev
Published in
5 min readJan 13, 2023

--

Understand how ViewModel survive to configuration change, after an Activity recreation for instance. It’s not magical and we will understand how this is done.

Using a ViewModel with the MVVM design pattern became the norm in the Android ecosystem for several years.

However, after conducting interviews with people with several years of experience in the Android development, I discovered that only a few people know how it works.

It’s good to know how to use a library, but it’s even better to know how it works under the hood.

The fact that a ViewModel survives to a configuration change is not magical, and we will understand the logic!

Where all the magic happens

The ViewModelStoreOwner interface

We use a ViewModelProvider to instantiate a ViewModel. The default way toinstantiate a ViewModel is the following:

Its constructor takes a ViewModelStoreOwner.

Here is the interface declaration:

The responsibility of a ViewModelStoreOwner is to:

  • Retain a ViewModelStore during configuration changes.
  • Call ViewModelStore.clear() when it is going to be destroyed.

It means that a ViewModelStoreOwner has a Lifecycle. When it’s in the Destroyed state, it notifies the ViewModelStore of its destruction, which notifies the ViewModel.

Here is a list of ViewModelStoreOwner implementations:

  • ComponentActivity
  • Fragment
  • NavBackStackEntry

Here is the code extract from ComponentActivity that listen its destruction and call ViewModelStore.clear() :

The ViewModelStore class

A ViewModelStore keeps a reference on a ViewModel by maintaining a HashMap<String, ViewModel>.

The String parameter, named key, is by default ViewModelProvider.DEFAULT_KEY:canonicalName.

We can specify a key when we call ViewModelProvider.get().

As described above, we pass a ViewModelStoreOwner when we create a ViewModelProvider. The ViewModelStore is retrieved from its owner.

When the ViewModelProvider creates a new ViewModel, it adds the ViewModel to the map in ViewModelStore.

How does it work in a ComponentActivity

Let’s choose the ComponentActivity class to dig inside and see how this works.

ComponentActivity implements ViewModelStoreOwner, so it overrides getViewModelStore().

mViewModelStore can’t be null. To avoid that, the method ensureViewModelStore() is called. If there was a previous ViewModelStore, it’s retrieved, otherwise, a new ViewModelStore is instantiated.

The interesting part is the call to getLastNonConfigurationInstance(). It’s a method of the Activity class that returns a NonConfigurationInstances. The class is declared in the ComponentActivity:

The custom variable was used for a previous behavior and will now be null every time.

The second parameter is of type ViewModelStore and is used to keep the reference of our ViewModelStore during a configuration change.

The documentation of getLastNonConfigurationInstance() says that it returns the result of the previous call to onRetainNonConfigurationInstance().

Here is the documentation of this method:

Called by the system, as part of destroying an activity due to a configuration change, when it is known that a new instance will immediately be created for the new configuration

And here is the code of the method:

We see that if there is an existing ViewModelStore, the method returns a new instance of NonConfigurationInstances containing the retrieved instance of the ViewModelStore.

We continue our way to find out who calls onRetainNonConfigurationInstance().

The caller is Activity.retainNonConfigurationInstances().

Here is the method body without the unnecessary code:

We see a class NonConfigurationInstances, but it’s not the same as the one of the ComponentActivity. This one is an inner class of Activity:

Small summary of the situation:

  • our ViewModel is stored in a ComponentActivity#NonConfigurationInstances
  • which is stored in an Activity#NonConfigurationInstance. This is done in the retainNonConfigurationInstances() of the Activity class.

Let’s dig inside the AOSP

To find the caller of retainNonConfigurationInstances(), we will have to change our investigation method.

Until now, I used the Find usage feature of Android Studio that looks inside the Android SDK code to find call hierarchy, but this technic gives no result for retainNonConfigurationInstances() because the caller is in the AOSP code.

Luckily, Google provides a website to dig into the AOSP code. You can find it here.

Now, we can find that the caller of retainNonConfigurationInstances() is inside ActivityThread:

The result of retainNonConfigurationInstances() is set in an ActivityClientRecord. The documentation of this class says:

Activity client record, used for bookkeeping for the real {@link Activity} instance.

It’s a POJO containing the Activity itself, its state, and many other information.

Now, we have to find where this ActivityClientRecord is kept.

We will follow a long road across several method calls:

  • ActivityThread.handleDestroyActivity()
  • ActivityThread.handleRelaunchActivityInner()
  • ActivityThread.handleRelaunchActivity()
  • ActivityRelaunchItem.execute()
  • ActivityTransactionItem.execute()

This last method internally calls getActivityClientRecord(), which calls ClientTransactionHandler.getActivityClient().

ClientTransactionHandler is an abstract class, and one of the implementations is a class we already saw: ActivityThread.

This ActivityThread keeps a map of ActivityClientRecord.

So, we finally found that our ViewModel is stored in the ActivityThread that is a singleton so the ViewModel is not destroyed during a configuration changed.

Here is the documentation of ActivityThread:

This manages the execution of the main thread in an application process, scheduling and executing activities, broadcasts, and other operations on it as the activity manager requests.

Restore the ViewModelStore

Following what we discovered just before, here is the situation:

  • our ViewModel is stored in a ComponentActivity#NonConfigurationInstances
  • which is stored in an Activity#NonConfigurationInstance.
  • which is stored in an ActivityClientRecord.
  • which is stored in ActivityThread.

When an Activity is recreated, its method attach() is called. One of the parameter is an Activity#NonConfigurationInstances. It’s retrieved from the ActivityClientRecord associated to the Activity.

If we look to the code from the beginning of our adventure.

We have ComponentActivity.ensureViewModelStore() that calls getLastNonConfigurationInstance().

This mLastNonConfigurationInstances has a variable instance that is in fact our ComponentActivity#NonConfigurationInstances that contains our ViewModelStore. We now know the full logic that is executed to save and restore a ViewModel 🎉🎉🎉

Conclusion

The journey was long, but we finally dug deep enough to understand the logic behind the “magic” of the ViewModel survival after a configuration change.

We had to dig into the AOSP code to have the big picture, but thanks to Google, which provides a friendly website to navigate in the AOSP code, even this part was easy.

Now that the ViewModel has no more secrets, I encourage you to understand the code of the different SDKs you use to truly understand their behavior. I’m sure there are many things to discover and learn from the code you will watch!

--

--

I’m a French Android developer at MWM. I humbly try to contribute to the developer community from which I learnt everything.