A Primer to ViewModel and LiveData in Android
By Yawei Li & Shaurya Arora
ViewModel
and LiveData
are classes provided as part of Google’s Android Architecture Components. The ViewModel
class is quite simply used to hold the model for the view — UI-related data that outlives Activity/Fragment recreation due to configuration changes (screen rotation, split-screen mode, and window resizing on Chromebooks/foldable devices). The LiveData
class, on the other hand, allows for an emitter-observer pattern: elements of the view can subscribe to data in the ViewModel
and be notified whenever that data changes. Let’s take a closer look at each of these 2 classes.
ViewModel
The ViewModel
class is an integral part of the MVVM (Model-View-ViewModel) architecture pattern that has been endorsed by Google for some time now. A view model (ViewModel
object) holds all the data that the view needs along with any associated logic. Usually, this data is exposed as LiveData
objects (more on that in the next section).
This is a simple example of a ViewModel
object. For some context, this is the view model for a screen that displays a list of NBA teams — this is all dummy data. A loading spinner is used until the teams are ready to be displayed, and an error message is shown in case of any issues loading the data. This view model is obtained in an Activity/Fragment as follows:
Using ViewModelProviders
ensures that the same ViewModel
object is returned even if the Activity/Fragment is recreated due to a configuration change. Note here that only the view holds references to view models; the view models don’t hold references to the view. This is a key difference between MVVM and MVP (Model-View-Presenter), since in MVP both the view and the presenter hold references to each other.
LiveData
The LiveData
class is essentially an observable data holder. Components can subscribe to, or observe, a LiveData
object and they will be notified automatically whenever the LiveData
’s value changes. Most importantly, LiveData
is lifecycle-aware: whenever its value changes, updates are delivered automatically only to those observers that are in an active lifecycle state (“Started” or “Resumed” states). Let’s expand on our TeamViewModel
class from above:
For every data point in the view model that we wish to expose, we usually maintain an internal MutableLiveData
object. MutableLiveData
is a subclass of LiveData
that provides a setter method for the underlying value; we cannot set the value on a LiveData
instance directly. However, since all logic for setting a LiveData
’s value should be encapsulated in the view model, we don’t want to expose this mutability externally. Therefore, we also write a function that exposes the MutableLiveData
object only as its super-type (LiveData
).
In the above example, isLoadingLiveData
is initialized only when a component starts observing it through the isLoading()
method. After initialization, the first value assigned to it is true
. Here’s how the activity can observe this loading state:
Each time the value of the LiveData
object returned by isLoading()
— which is really just isLoadingLiveData
— updates, the code in the Observer
above is executed automatically. The updated value may also be null, so we add a ?.let{}
block. In most cases, onCreate()
is a good place to start observing a ViewModel
’s LiveData
s.
Here’s a noteworthy excerpt from the official Android documentation on LiveData
:
Generally, LiveData delivers updates only when data changes, and only to active observers. An exception to this behaviour is that observers also receive an update when they change from an inactive to an active state. Furthermore, if the observer changes from inactive to active a second time, it only receives an update if the value has changed since the last time it became active.
To paraphrase, updates in the value of isLoadingLiveData
will be delivered to MainActivity
only if MainActivity
is in an active state (“Started” or “Resumed”). In addition, MainActivity
will receive an update the first time it goes from an inactive (“Paused or “Stopped”) state to an active state. However, subsequent inactive –> active transitions in MainActivity
’s state will warrant an update from isLoadingLiveData
only if the underlying value of isLoadingLiveData
has changed since the last time MainActivity
was active.
To wrap things up, let’s look at a slightly more complicated example:
UserInformationViewModel
exposes a LiveData
for a single User
, which, as we learn from the loadUserInfo()
function, comes from either the database or from the network. Notice that we initialize isLoadingLiveData
as true
and isErrorLiveData
as false
so that the observing component (Activity/Fragment) knows to initially show some loading spinner and hide some error view. Moreover, as soon as the first observer subscribes to userLiveData
(via getUser()
), we initialize userLiveData
and call the loadUserInfo()
function. Finally, inside loadUserInfo()
we set our loading state, error state, and User
object as necessary. Ideally, one would use Coroutines or RxJava to wrap database and network operations in worker threads.
And that’s it for a quick primer! Head over to part 2 of this post where we convert a sample project from MVP to MVVM using ViewModel
and LiveData
.
Yawei Li is a Toronto-based Android developer and an avid dog lover.
Shaurya Arora is a Toronto-based Android developer, drummer and prog metal/djent lover.
Join our fast growing team and connect with us on Twitter, LinkedIn,Instagram& Facebook! Learn more about us on our website.