Skip to content

Instantly share code, notes, and snippets.

@Struka9
Last active April 10, 2019 21:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Struka9/2369ba2367429a38f8b383bc4e357696 to your computer and use it in GitHub Desktop.
Save Struka9/2369ba2367429a38f8b383bc4e357696 to your computer and use it in GitHub Desktop.

The Gentle Guide to ViewModel

Hello, guys, I had so much fun writing my previous article about LiveData that I started to think about the next one right away, the obvious choice for the topic was the next component of the Android Architecture Components framework so I thought about making it a series where we will cover all of them, so this is the second part of the series where we will talk about ViewModel

Overview

ViewModel is a class that has been designed with the single goal of storing data that is related to the UI in a lifecycle aware manner, it’s implemented in such a way that it outlives any configuration change, such as device rotation.

Why do we even need it?

Android Framework handles the lifecycle of ::Activities:: and ::Fragments::, which from now on we will refer to in-distinctively as the UI Controller, and it can decide to destroy and recreate them in response to events that are completely out of the developers control, any transient data is then lost. In some occasions this can lead to poor performance as any data that is lost in this way will need to be retrieved once again (think about having to retrieve a long list of users from the network every time the device is rotated). Of course, the framework provides the ::onSaveInstanceState():: method to save data across instances of the UI Controller, but it has some limitations as it can only handle simple data that can be serialized and deserialized.

Another problem that arises from this paradigm is that usually the UI Controller gets more responsibilities than it ideally should, let’s say we got a fragment where we show some user data retrieved from the network, this MUST be retrieved in an asynchronous way and it’s not uncommon to see that the responsibility of making, handling and cleaning these calls lies in the UI Controller. This kind of approach should not be used as the UI controller should limit it’s responsibilities to show data, get inputs and maybe some interface with the framework, as when requesting permissions.

There must be a better way…

Android Architecture Components introduced a helper class for the UI Controller that is responsible of getting the data to show in the UI. ViewModel objects outlive the lifecycle of the UI Controllers and are retained during configuration changes, so they are are ready for the next instance of the UI controller, this is why they should be delegated any work consisting on getting data.

If the UI Controller is recreated it will receive the very same instance of the ViewModel bound to it’s scope. Let’s say we got an activity in which we retrieve a ViewModel instance, as long as the activity is not finished it will receive the same instance of the ViewModel everytime it’s recreated. This is very important as ensures we can forget about having to retrieve the data every time the UI Controller is destroyed due to configuration changes. Once the UI controller is no longer used (finished in the case of activities and detached in the case of fragments) the framework will call ViewModel’s onCleared() method, giving you the chance to perform any cleanup routine you need.

Let’s see a sample from the Android Docs, that’s is simple enough to illustrate how you would use ViewModel
https://gist.github.com/2bbc08574435cbb15509ebebc5ef99fd

It’s important to note here the call to the of() static method in the ViewModelProviders class, as you can see a reference to the ::Activity:: is passed, this is used to determine the scope of the ViewModel, ViewModelProviders also got an of(Fragment) method so you can get a ViewModel instance in any scenario.

ViewModel can contain any LifecycleObserver object, as LiveData, but it should never be bound to a specific instance of a UI Controller, as it can be a source of memory leaks. There are some cases in which you would need to get a ::Context:: to work with in the ViewModel, in these cases you should extend AndroidViewModel instead, it will receive an Application object in it’s constructor.

Lifecycle of ViewModel

Sharing can be complicated

It’s a common scenario that two or more fragments need to communicate with each other sharing objects and depending one on another to do their work. Usually you would need to define some kind of interface with callbacks to let them know what it’s going on with each other, also the activity containing them would need to perform the “binding” between the fragments and ,as this was not enough, each of the fragments would need to handle the cases where the other have not yet been created or are not visible.

Let’s say that you got two fragments, FragmentA and FragmentB; in FragmentA you present a list of Users and in FragmentB, once a user is selected, you show it’s details, instead of following the method described above you could use a ViewModel containing the selected User, but instead of binding it to one given ::Fragment:: you would bing it to the ::Activity:: instead, in this way both of the fragments will get the same instance!

https://gist.github.com/a039f6d41bbc231ef2c265646bf7f893

Right away this method has several advantages over the tedious process described before:

  1. The activity doesn’t need to know about the communication, this translate to less code to write and less sources of potential bugs
  2. The fragments don’t know about each other, this means that at any time each of the them can be updated or even completely replaced independently and, as long they respect the contract to the ViewModel, they will continue to work fine
  3. We don’t have to handle the cases in which any of the fragment is not yet created or visible.

As we can see using ViewModel simplifies a lot of the work that has to be done in a wide range of scenarios, and since it’s supported directly by the framework it’s worth to be included in all of your projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment