Android ViewModel. How it works.

Vladislav Puryev
15 min readJan 18, 2020

--

Hello everybody.

This post will be about popular solution from Google for Android developers — ViewModel (if you are not familiar with it yet — get started from official documentation). The main goal of article — show you how viewModel works, how this tool helps us to save state of our screens.

First of all it is better to note that viewModel — is just java code inside Android framework and there is no any magic… almost.

This mechanism was improved something about three times from its origin (2017) till the present day. Let’s take a look atEvolution of anatomy”.

WARNING! This article contains a lot of technical content and internals of Android framework.

And please pay attention — all demonstrated code you can find in your Android Framework using middle-click or ctrl + left-click. There is no any of self-made code — all is inside Android framework.

Attention again — most of code will be provided in shortened form. Only necessary details. Full versions you can find in Android Framework source codes straight inside your IDE.

ViewModel 2017

Honestly, this part (I mean exactly ViewModel’s version of 2017) can be unnecessary, because Medium has several articles, that describes viewModel saving state mechanism in first version. I like this one. But I suggest you more code than there.

First of all let’s take a look at ViewModel class itself. According to official documentation:

The ViewModel class allows data to survive configuration changes such as screen rotations.

It can lead to research this class.

ViewModel class

It is just simple abstract class. Without any secrets. With one empty method.

Can documentation be wrong (or obsolete) so long time?

This question is not so obvious. From one side, if you will create your child viewModel and initialize it using constructor, in this case saving states mechanism will not work. But if you are doing everything in the right way (using special initialization with of() method in ViewModelProviders class and custom factory for extra dependencies) — then everything will be fine. To be honest — yes, ViewModel class doesn’t contain any special mechanism for saving state inside itself. But it is the result of group of processes inside Android framework, that were developed for saving dynamic instances of different data. In this point of view ViewModel really helps us to save states. But how does it survive throughout configuration changes?

To find out this, it is better to start from top of iceberg — from method of() in ViewModelProviders class. There are two methods: one for Activities and one for Fragments.

ViewModelProviders methods of()

It is logical, that this method will return a new instance of ViewModelProvider. After that we can call get() to retrieve viewModel instance. We should look inside ViewModelProvider.

ViewModelProvider method get(modelClass)

Everything is simple: create a special string to pass it… into another one method get(). Let’s see inside.

ViewModelProvider method get(key, modelClass)

ViewModel instance is provided by object that called mViewModeStore.

Please note: this method get() is public and you can use it for getting different instances of the same viewModel types. Same viewModel class, but different fields values. Key feature is a key.

Maybe it is good idea to look at class ViewModelStore? NO. This store is just HashMap wrapper, no more, no less. No any special things that can help to save data throughout configuration changes.

Answer is in ViewModelProvider. It gives a ViewModel instance, that ignores Activity/Fragment lifecycle. And ViewModelProvider retrieves this from ViewModelStore. This ViewModelStore is “immortal” (almost). But how?

Let’s research a process of injection ViewModelStore into ViewModelProvider. It need it in this constructor. And, if you notice (see code “ViewModelProviders methods of()” above), it gets ViewModelStore instance inside method of(), with help of another method of() that is member of class ViewModelStores (pay attention — the store for instances of ViewModelStore).

ViewModelStores method of()

This class contains only two almost the same methods. All difference — one takes an FragmentActivity instance, another takes Fragment instance. That is why it is enough to demonstrate just one of them.

There is an if statement. If it is true the ViewModelStore instance will be got from Activity/Fragment instance, because it is an ViewModelStoreOwner interface implementation (the interface with just one method). But it applies to Activities/Fragments that can be instances of this. When first version of ViewModel was created, these Android components were not implementations of ViewModelStoreOwner. That is why at the beginning (because over time Activity and Fragment starts to implement interface, but anyway it is needed to support old versions) condition didn’t exist. And even after that old versions ignore if statement.

There is a mysterious method holderFragmentFor(). It returns something that really has getViewModelStore() and can give us a demanded store. What is inside?

HolderFragment methods holderFragmentFor()

There are two methods static holderFragmentFor() inside class HolderFragment (that is just simple fragment). And they call methods with the same names of static field with type HolderFragmentManager.

Again — try don’t get confused. Method holderFragmentFor() of HolderFragment call holderFragmentFor() of HolderFragmentManager. Anyway the last mentioned class is not interesting. Because it is using just for creating new instances of HolderFragment and retrieving them from special hashMaps (one — for activities and one — for fragments) if they exist. There is no any handling of configuration changes. Finally, for getting answer the long way leaded to top of HolderFragment class.

This fragment initializes new viewModelStore above its constructor and has one special call straight inside constructor:

HolderFragment declaration

When you call get() from instance of ViewModelProvider to take new ViewModel, you try to retrieve it from ViewModelStore that was passed into constructor of ViewModelProvider. As viewModelStore is retained, because was initialized in retained fragment, all its content can survive configuration changes.

Not bad, really? To use the existing mechanism to save state. If you have never used retained fragments, but started to use ViewModel in your code, just know — you were using retained fragments (but didn’t suspect it).

Let’s move to next step…

ViewModel 2018

As was mentioned before: not all activities and fragments implemented ViewModelStoreOwner. But in summer of 2018 the massive migration on AndroidX was performed. Now FragmentActivity and Fragment become ViewModelStoreOwner implementations. If so, why does framework need holderFragments?

Commit for removing HolderFragment
Commit for removing HolderFragment

There is no HolderFragment in Android framework anymore. Class ViewModelStores became deprecated (but if you are using androidx.lifecycle package you still can see this class inside framework — don’t have any idea why). It is possible that Google developers team had extra reasons for this refactoring, but for now it looks like part of AndroidX.

If holderFragments are not exist it means that there is other saving states mechanism in Android framework.

To find out the new process it is better to start from familiar function of() in ViewModelProviders.

ViewModelProviders methods of() in 2018

Everything is quite predictable. Let’s move further. Start from Activity getViewModelStore() implementation.

Saving ViewModelStore instance for Activity

First of all — a whole scheme of interactions to get ViewModel instance for Activity in 2018.

Getting ViewModel instance for Activity in 2018

You are getting a new instance of ViewModelProvider. It gets an instance of ViewModelStore into constructor from FragmentActivity. Finally it is possible to retrieve a viewModel, which retains the data. Quite simple, instead of HolderFragment ViewModelProviders.of() is using FragmentActivity. Let’s take a look at implementation.

FragmentActivity method getViewModelStore()

We can see something new here (or not?). Code is not complicated, but there are two interesting lines. From object type we can guess that NonConfigurationInstances is something, that ignores Activity lifecycle. It is not necessary to look at class declaration, because it is just a container for viewModelStore and something else, but no more. More interesting to investigate method getLastNonConfigurationInstance() (please, be ready for many similar names later). It is a function in the basic Activity (that is ContextThemeWrapper) and has curious comment above it.

/**
* Retrieve the non-configuration instance data that was previously
* returned by {
@link #onRetainNonConfigurationInstance()}. This will
* be available from the initial {
@link #onCreate} and
* {
@link #onStart} calls to the new instance, allowing you to extract
* any useful dynamic state from the previous instance.
*
* ...
*/

And it has next code inside.

Activity method getLastNonConfigurationInstance()

Don’t think about this returning value. Just know — it can be any object, that will be returned by… mysterious onRetainNonConfigurationInstance(). It’s declaration must answer on all questions about new saving state mechanism. But let’s start from comment again.

/**
* 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. You
* can return any object you like here, including the activity instance
* itself, which can later be retrieved by calling
* {
@link #getLastNonConfigurationInstance()} in the new activity
* instance.
*
* ...
*
*
@return any Object holding the desired state to propagate to the
* next activity instance
*/

Called by the system. It looks like some sort of magic. We can’t have any control over this. Okay, but we know, that the object that is saved by this method, will be provided by getLastNonConfigurationInstance().

This method return nothing. When I have seen this line, I thought that my research is ended.

But… Android framework generally written on Java (not Kotlin). It means just one thing — this language don’t have open modifier for methods overriding. And it is overridden in FragmentActivity.

FragmentActivity method onRetainNonConfigurationInstance()

It looks very elegant (don’t forget with a pinch of magic). This method is called between Activity lifecycle methods: onStop() and onDestroy().

And let’s look at scheme.

Retrieving viewModelStore instance when Activity created first time

When Activity created there is no any instance of ViewModelStore inside. That is why when getViewModelStore() called, it gets a null from getLastNonConfigurationInstance(). In this case the new ViewModelStore is creating using constructor.

Destroying Activity

During process of Activity destroying method onRetainNonConfigurationInstance() is called by system. It initializes a new NonConfigurationInstances, which gets a value of mViewModelStore, that was previously created in getViewModelStore(). And then it returns this object to System.

Creating a new instance of Activity

In new Activity getViewModelStore() will get the retained viewModelStore because getLastNonConfigurationInstance() return a non null object. One more time — because onRetainNonConfigurationInstance() was called earlier and it saved a ViewModelStore.

Have you ever seen these two interesting methods before? Okay, may be you have used them? According to the official documentaton for getLastNonConfigurationInstance() and onRetainNonConfigurationInstance() they are deprecated for us (not for Android framework developers). And they were existing from the first API. But as you can see, they are still useful (but onRetainNonConfigurationInstance() is final). And if you want to use something like these two magic methods, then Android framework has something special for you (beside of retained fragments and viewModels). Two methods with longer names (word “custom” added, try don’t be confused) that are in FragmentActivity.

FragmentActivity alternatives for methods …NonConfigurationInstance()

Pay attention, that onRetainCustomNonConfigurationInstance() is not final. You can get the same result as with ViewModelProviders.of(modelClass).get() combination. If you are interested in this feature — try the sample at GitHub.

Well, it was all about saving ViewModel if we mean Activity. For fragment the situation is a little bit different.

Saving ViewModelStore instance for Fragment

WARNING! This part of article will hide most of code by schemes, because some methods are quite big and have a lot of details, and many of them are not important for saving instances of ViewModel. Use Android framework source code if you would like to see the code.

Now ViewModelStore is provided by Fragment itself (cause it is an implementation of ViewModelStoreOwner). That is why ViewModel instance initialization looks like at next scheme.

ViewModel initialization for Fragment
Getting ViewModel instance for Fragment in 2018

Please pay attention: the scheme above is very similar on previous scheme for Activity (to be precise — FragmentActivity), try don’t mix up FragmentActivity and Fragment.

Let’s look at getViewModelStore() implementation.

That time nothing special inside. And Fragment class does not contain anything that can create or retrieve “immortal” instance of ViewModelStore. Only new one with constructor. But this field has default access modifier. Therefore something can initialize it. Now all processes of saving viewModelStore start with destroying an Activity instance (yes, exactly Activity, time to rotate a device).

Saving ViewModelStore instance for Fragment
Saving ViewModelStore instance for Fragment

When Activity is being destroyed, then method onSaveInstanceState() is calling (somewhere between onPause() and onStop() or after onStop() if application is targeting on Android.P and higher). It contains field of type FragmentController that looks like just a wrapper for FragmentManager methods. Callback onSaveInstanceState() call method saveAllState() of FragmentController, which call method with the same name, but from FragmentManager instance. Function saveAllState() of FragmentManager (it is very difficult don’t to mix up all of it, isn’t it?) call method saveNonConfig() almost in the end. And there are all the secrets.

Function saveNonConfig() of FragmentManager check all fragments to find their ViewModel stores. If it is not null (and it can’t be null, if getViewModelStore() was called for Fragment first time), then it will be collected into special ArrayList. And after that, if ArrayList is exist (see this method logic), then new instance of FragmentManagerNonConfig will be created with all ViewModel stores inside. FragmentManagerNonConfig is simple class-container for ViewModel stores, child fragmentManagerNonConfigs and… retained fragments. There is no HolderFragment, but generally mechanism is not very different. Let’s move further.

After saving FragmentManagerNonConfig instance, next steps are performed inside familiar function onRetainNonConfigurationInstance(). It calls method retainNestedNonConfig() of FragmentController, that wrap method retatinNonConfig() of FragmentManager. As a result, FragmentManagerNonConfig instance will be value for field of NonConfigurationInstances variable inside onRetainNonConfigurationInstance().

Retaining FragmentManagerNonConfig inside method onRetainNonConfigurationInstance()

This instance of FragmentManagerNonConfig will be returned with ViewModelStore instance, that survives throughout destroying of Fragment, due to described interactions. But where? One more scheme is coming.

Restoring ViewModelStore instance for Fragment

You can see something very familiar on the scheme above. There is no mistake. Method onCreate() invokes getLastNonConfigurationInstance(). But it means callback of FragmentActivity. Okay, viewModel stores for fragments ignore lifecycle due to interactions between two earlier mentioned methods with long names (onRetainNonConfigurationInstance() and getLastNonConfigurationInstance()). But let’s see on all steps (they are passing through many many lines of code).

Inside onCreate() (you are tired of it, aren’t you?) instance of NonConfigurationInstances is retrieved from getLastNonConfigurationInstance(). After that the value of field fragments (field inside NonConfigurationInstances), if it isn’t null, will be passed as a parameter to method restoreAllState() of FragmentController. Please note, that fragments has type FragmentManagerNonConfig. FragmentController, as expected, will call method with the same name, but from FragmentManager, and will pass FragmentManagerNonConfig instance into this function. And there, inside this function restoreAllState() of FragmentManager, all fragments are creating from scratch, using special fabric method of class FragmentState. This method is called instantiate() and gets ViewModelStore instance, that was previously retrieved from FragmentManagerNonConfig (to be precise — from the list of ViewModelStore inside this class), that was passed to restoreAllState().

Yes, all fragments become new instances, but they all get a value for mViewModelStore (that has default access modifier) from FragmentManagerNonConfig instance, that was retained due to two main methods. New fragments with old ViewModel stores.

In the end of this scheme, inside onCreate(), method dispatchCreate() is called, but it needed just for set a appropriate Fragment state according its lifecycle. And it doesn’t have any connection with saving ViewModel.

It was in 2018. But some changes have occurred in 2019…

ViewModel 2019

First of all, please allow to thank you. You have deserved a candy for your patience and desire to find out all secrets of saving states within Android ViewModel. You are almost getting the goal.

It is not possible (for now) to have a complete understanding why Google developers decided to perform new refactoring. Maybe they didn’t like that Fragments had fields of type ViewModelStore. Maybe the code for retaining ViewModel stores instances was too complicated (and of course it was, keep reading). Maybe something else.

But new version of android support library (Appcompat) have come on the 5th of September 2019. And something changed (unfortunately methods custom, that were mentioned earlier became deprecated, but still are in system).

First of all ViewModel class had been changed. But it still doesn’t contain any function or special code that have influence on “surviving” of viewModels.

ViewModel class in 2019

Mechanism of saving and restoring viewModels for Activity was changed, but a little bit. All difference is in that now not FragmentActivity is implementation of ViewModelStoreOwner interface, but its parent — ComponentActivity. That is it.

(Okay, method getViewModelStore() has some modifications, but they are very very minor and generally everything is the same as one year ago).

Process of saving and restoring viewModel stores in case of Activity (2019)

But more interesting situation happened to fragments. Of course, it is necessary to look at The Great method getViewModelStore().

First of all, Fragment class doesn’t contain field mViewModelStore. Yes it implements ViewModelStoreOwner, but it retrieves demanded instance from another getViewModelStore() — in FragmentManagerImpl. Yes, it is an FragmentManager child, but it has its own method getViewModelStore(). But it isn’t an implementation of ViewModelStoreOwner. It just has this method. And next code is inside its body…

Method getViewModelStore() in FragmentManagerImpl

This function calls another one function with the same name. And it belongs to field, that called mNonConfig. This field has type FragmentManagerViewModel. It means that all interactions for retaining and restoring ViewModelStore instance now are looking like on next scheme.

Process of saving and restoring viewModel stores in case of Fragment (2019)

This new field is just… child of ViewModel class. Surprise! To get instance of ViewModel, that will ignores Fragment lifecycle, we need another ViewModel. Well, may be it sounded hopeless, but nothing wrong. All “magic” (okay, full lack of it) is not inside method getViewModelStore() of mNonConfig. It is important how this instance is initialized and why it is in retained state. Let’s kill… Activity again.

Do you remeber scheme above that has title “Saving ViewModelStore instance for Fragment”? No? Do you want to look it again? No? Perfect. Because all wonders are happening, when new instance of Activity is creating.

Initialization of FragmentManagerViewModel

When Activity is created, the method attachHost() of FragmentController is called inside onCreate() callback. And sure, it calls another attachController() of FragmentManager. This method (FragmentManager) gets field, that called mHost (actually, it gets mHost twice, but no matter). This field has type FragmentHostCallback.

The initialization of FragmentManagerViewModel is happening inside method attachController() of FragmentManager. But to create new (or not) instance of FragmentManagerViewModel, it needs a ViewModelStore. And it gets store from… another one getViewModelStore() of FragmentHostCallback. After that factory method getInstance() provides FragmentManagerViewModel. But, if you take a look inside FragmentHostCallback, you will see that this class doesn’t have anything, that can be connected with ViewModelStore. Even getViewModelStore(), because it doesn’t implement interface ViewModelStoreOwner. How can it be? Using type casting, of course.

Method attachController() in FragmentManagerImpl

But what is the host? How can it be cast to ViewModelStoreOwner instance? Let’s take a look, what FragmentController pass to the method attachController() of FragmentManager. It gets it from constructor. But it has private one. And there is one fabric method, that called createController(). And this method gets FragmentHostCallback instance. What passes it to this function? It is FragmentActivity.

Initialization of FragmentController inside FragmentActivity

It is logical, that HostCallbacks instance is a child class of FragmentHostCallback. And it is inside FragmentActivity (inner class). What is inside this class?

That is it! I am completely sure, that you have been suspecting this. No any special inside fragments. All processes of retaining ViewModel instances (and as the result — retaining screen states) are depending on Activity.

All right, all right, man! And so what?

Yes, this article covered mechanism of saving states, using Android ViewModel. All versions of it. It is good, but what is so useful in this big text?

I can tell you, that it is very useful to learn code of Google software engineers. I also can tell you, that I have showed you some new methods, that maybe you haven’t seen before, because they are not so popular, comparing with ViewModel itself.

And finally I can tell you, that ViewModel retaining mechanism is built on very old and powerful solution inside Android system, it is good to know that all code is just code, and no magic inside any library or class.

BUT! I really have knockout answer on this question (I want to thank my Team Lead Anton, who asked this great question first). Are you ready? It is better to sit down.

Never try to override method getLastNonConfigurationInstance() of Activity.

Yes, documentation tells us, that this method is deprecated. But it is not deprecated inside your Android framework (for the moment of writing these lines). It means, that IDE will not hint you that this method is deprecated. And that is why it is very easy to miss this. If you will override it, in the best case your ViewModel instances stop retain data. In the worse case, you will get an exception. Or, may be, anything else will happen inside your application. Official documentation doesn’t warn us about it. That is why: be smart, be safe, don’t try this in your code, especially at work.

It is the end of this long article. Hope, that it was useful and interesting for you. Thank you for your time. Stay well and have a good mood.

See you again.

Thank to my brother, who reviewed this article first.

--

--