ViewModels under the hood..

Dario Mungoi
Google Developer Experts
9 min readFeb 12, 2018

Last year at Google I/O, Google announced a set of libraries named Android Architecture Components.

The Android architecture components were created for the following reasons. (This is how I perceive them and it does not reflect Google or its engineers’ actual opinions):

1 . Give an opinion on how to architect an android app. There are many different ways to architect an android app and many of those were not mentioned in the docs. This confused a lot of new Android developers trying to use only the documentation for learning.

2 . Provide tools to help in solving common problems found by android developers. (lifecycle management, data persistence, paging)

Android Architecture Components is the combination of the libraries: Room, ViewModel, LiveData, Lifecycle aware components and the Paging Library.

Last year, I gave two presentations about these components. These were focused on the ViewModels and the Lifecycle aware components(Link to one of the recordings)

After the presentation, the audience would normally have two feelings. Excitement for finding a solution to solve their problems with ViewModels, and curiosity to understand how they work.

To answer to that curiosity and other common questions such as:
-Why should we not store too much data inside the ViewModel?
- Why can we not use ViewModels to communicate between Activities?

I decided to write this post and explain what happens under the hood when using ViewModels.

This post will not talk about the other components in the library. There are so many resources available and some of the links to these will be posted at the bottom of this post.

ViewModel

The ViewModel class is designed to store and manage UI-related data so that the data survives configuration changes such as screen rotations. — developer.android.com

Suppose you have an application with a screen to show the details of a Movie.
When opened, this screen will display the information loaded from a source (network/database). If the user decides to rotate the device, the information will be reloaded from the source.

This is because the Android system will recreate the Activity on the rotation configuration change.
That small behavior repeated many times, can waste the users’ data if the source is a remote server. This is one very small problem but a lot more damage can be done in more complex cases.
To solve this small problem and make our app a good citizen on the users’ device we could try to do the following:

1. Make the class Movie a Parcelable object and save it in the instance state Bundle.

2. Process configuration changes on our own.

3. Create a ViewModel, keep the Movie object in the ViewModel and let it do the rest.

The first solution is the easier to implement and it is the most used for simple cases like our example. However, it is easy to hit the limit size of a Parcelable if the objects become too complex.

The second solution would prevent the Activity from restarting. It would solve our problem but we lose in the process other features that could benefit from the Activity restart(Reload appropriate resources for the new configuration).

The third solution using the ViewModels is the most reliable and easier to implement even in the most of the complex cases. To implement this solution we would go through the following steps:

1. Create a subclass of the ViewModel class that has a Movie as a variable.

2. Expose a method on the ViewModel that takes the Id of the movie details it should load.

3. Expose a LiveData that emits a Movie object loaded in the ViewModel.

4. Get an instance of the ViewModel inside the Activity/Fragment.

5. Observe for changes from the exposed LiveData inside the Activity/Fragment.

6.Fetch the Movie from the source and wait for it to be posted by the LiveData.

7.Finally, display the movie details when the Movie object is posted to the Activity/Fragment were we are observing from.

Now, if the device is rotated the ViewModel will survive the configuration change and it will exist as long as the scope it is associated with still exists. As shown in the image below the ViewModel will stay alive until it’s scope(Activity or Fragment) is destroyed.

ViewModel Scope

This means we will get the same ViewModel instance. We will start observing this instance LiveData and the latest emitted data before the rotation will be emitted.

Those seven steps are relatively easy to do. But they do not answer the questions I mentioned earlier.
To do that, let’s see how this process happens in details.

The process of creating/getting a ViewModel Instance

Imagine we have a MovieDetailsViewModel class. We can get its instance by executing this statement from a Fragment/Activity:

ViewModelProviders.of(this).get(MyViewModel.class);

The chained calls in the statement above can be divided into two separate parts:

1st — A call to get a ViewModelProvider using the static method of in the ViewModelProviders class.

2nd — A call to get the ViewModel from the ViewModelProvider retrieved on the first call.

ViewModelProviders

The ViewModelProviders is a class with helper methods to get a ViewModelProvider.
This class has the DefaultFactory as it’s only private variable. The DefaultFactory class is used to create a new Instance of a ViewModelProvider.
ViewModelProviders have an overloading of the static method named of. One of the methods takes an Activity and the other a Fragment as a parameter as shown on the following signatures:

public static ViewModelProvider of(@NonNull FragmentActivity activity)
public static ViewModelProvider of(@NonNull Fragment fragment)

When calling the of the method of to get a ViewModelProvider, the following will happen:

  1. Check and get the Fragment/Activity Application.
    For an Activity, this check and retrieval is made by calling the checkApplication(Activity)method.
    This method, calls the Activity getApplication() which will return the application or throw an exception if null.
    When calling the of method passing a Fragment, a similar process is used. In this case, the getApplication parameter is a wrapped call to checkActivity(Fragment). The checkActivity method will get the Activity for the Fragment. This will ensure the Fragment is attached to an Activity first.
  2. Initialize the ViewModelProvider Factory.
    To do this, the initializeFactoryIfNeeded(Application) method will be called. This method takes as a parameter the Application instance retrieved in the previous step.
    3. Return the ViewModelProvider.
    The of method will create and return a new instance of the ViewModelProvider.

This step complete implementation to get a ViewModelProvider for an Activity looks like this:

Snippets from take from ViewModelProviders implementation.

ViewModelProvider

The ViewModelProvider, as the name implies is the class that actually provides the ViewModel.

This class, contains the static method get(Class<T> modelClass). This is the method invoked in the second part of the statement to get a ViewModel mentioned earlier.

From the exposed API, everything is pretty simple. But there is way more happening before we get that instance of a ViewModel as we are going to see next.

The ViewModelProvider have two constructors:
1.ViewModelProvider(ViewModelStore store, Factory factory)
2. ViewModelProvider(ViewModelStoreOwner storeOwner, Factory factory)

Both constructors take the Factory to create a ViewModel class as the second parameter but differ in their first parameter.

The first constructor takes a ViewModelStore. A ViewModelStore is a class that stores ViewModels.

To save ViewModels, the ViewModelStore stores them in a HashMap. Each ViewModel is saved in the map using a String as key.
The key is the concatenation of the DefaultName variable and the ViewModel class canonical name. (DefaultName is a simple final string inside the ViewModelProvider class)
A ViewModelProvider created with a ViewModelStore will return the ViewModel by doing the following:

1. A key will be created using the DefaultName and the canonical name of the class passed as a parameter of the get method.

2. Use the key to retrieve the ViewModel Instance from the HashMap.

3. If found, Perform checks and ensure the correct ViewModel instance is returned. Otherwise, a new instance will be created, saved in the ViewModelStore, and returned.

Snippet from ViewModelProvider.java implementation

So far we know how the ViewModels are created and stored but,
this doesn’t yet explain the question of how they survive the configuration changes. This is because this first constructor of ViewModelProviders takes a ViewModelStore and this store on it’s own does not belong to any scope.

In the implementation to solve the problem we started with, the ViewModelProvider will be created using the second constructor. The constructor that takes as the first parameter a ViewModelStoreOwner

ViewModelStoreOwner

The ViewModelStoreOwner is an Interface and as the name implies is the owner of a ViewModelStore. This can be any class that implements the getViewModelStore() defined by this interface.
In the library, the ViewModelStoreOwner is the HolderFragment class. This class has a ViewModelStore variable that can be accessed by the getViewModelStore() method.
HolderFragment is a regular Android Fragment. This Fragment uses the setRetainInstanceState(boolean) method to true in order to retain the instance state. As you can guess, the state has the ViewModelStore with all the ViewModels it contains.

This technique has been used for the same purpose so many times in the past before the Android Architecture components.

HolderFragment is the scope where all ViewModels inside the ViewModelStore will live. According to the image with the ViewModel scope shown earlier in the post, All ViewModels will be cleared if the scope(HolderFragment) is destroyed.

Now we know how the ViewModels survive configuration changes. However, this doesn’t explain how we can do things such as communicate between Fragments with ViewModels and why we can’t do the same with two Activities.

Who owns the HolderFragment?

HolderFragmentManager

The HolderFragment has an inner static class named HolderFragmentManager. The HolderFragmentManager creates and managesHolderFragment instances.
After creating the instances it associates them with an Activity or Fragment.
The whole process is done using the methods holderFragmentFor(Activity) and holderFragmentFor(Fragment).
These methods will behave in two different ways if there is or not an instance of the HolderFragment.
For the case where there is no instance of the HolderFragment, these methods will:
1. Create an instance of HolderFragment.

2. Add the new instance to the parent(Activity/Fragment) FragmentManager. This will result in the expansion of the scope of the ViewModels inside the HolderFragment . As a result, the ViewModels are going to be alive as long as the Activity/Fragment that has them stored in its FragmentManager

3. Register a callback to the Activity/Fragment lifecycle onDestroy() callback method. BecauseHolderFragment is inside an Activity/Fragment FragmentManager when the Activity/Fragment is destroyed its onDestroy() method will be called and the ViewModelStore will be cleared. Lastly, the HolderFragment instance will be removed from the HolderFragmentManager HashMap that associated it with an Activity/Fragment when the registered callback is invoked.

4. Add the holder Fragment to a HashMap where it’s key is an Activity/Fragment. This is why we cannot communicate between two Activities or Fragments using the same ViewModel.

For Fragments, the communication is possible only if they can have the same parent. This means that we can cheat and get the same instance of a ViewModel by asking for the parent Activity instead of the Fragment itself.

5. Return the HolderFragment instance.

When there is already an instance of the HolderFragment these methods will look up and return the instance already in the HashMap.

Knowing the details below the ViewModelStoreOwner, we can go back to the ViewModelsProviders to see how we create the ViewModelStoreOnwer needed to create a ViewModelProvider instance.
The ViewModelProviders class gets an instance of the ViewModelStoreOwner by calling the static methods of from the ViewModelStores class.

The ViewModelStores class job is of abstracting the call to the holderFragmentOf(Activity/Fragment)static method inside the HolderFragmentManager. As mentioned before this method will be responsible for returning a ViewModelStoreOwner.

I hope you had a good time reading this long post and by now you will have a bit more of understanding of how the ViewModels are created and work.

My hope is that you can use the details on this explanation to always remember that:
— You should not try to get an instance of your ViewModel before your Activity is created. If getting it for a Fragment, you should not try to get it on a Fragment not attached to an Activity.
— You should not put too many things on your ViewModel. As you could see they are just a regular Fragment and if the system decides to kill because it is using too much resources its parent and all your ViewModels will be gone too. In order to be safe from this kind of problems, I advise you to use the ViewModel alongside the savedInstance state solution.

In our example, storing the movie Id in the instance state would ensure that even if the ViewModel is killed we will still be able to get the correct movie id and pass it to reload the data for the appropriate Movie.

— You can only use ViewModels to communicate between Fragments that share the same Parent.

If you feel like something was not clear, have any suggestions or something to add up, please drop your comments below and let’s get the conversation started.

Thanks for your time and feel free to share with a fellow dev you think would enjoy reading this post.

See you next time!

Useful links:

Architecture components official guide

Architecture components Intro — Google IO 17

Architecture components — GDD Europe 2017(Florina Muntenescu)

Android Architecture Components — Looking at ViewModels — PART 2 (Rebecca Franks)

Intro to Android Architecture components — SSA Experts on Air Episode 6

Thanks to Florina Muntenescu, Rosário Pereira Fernandes and Mustafa Ali for reviewing the first version of the post.

--

--

Dario Mungoi
Google Developer Experts

Senior Android Developer at @Shopify, Design Sprint Master, former Android GDE