Introducing LiveData

However, even those “best practices” have a bit of a gap: how do we push data from a ViewModel to an activity or fragment? More importantly, how do we do so while taking into account configuration changes?

So far, our viewmodels have been simple state containers. The activities and fragments pull data from the viewmodels and push data into them.

Now, though, if we are saying that the viewmodel is somehow getting data delivered to it from the background, somehow we need the activity or fragment to find out that the data has arrived. So, for example, suppose that our fragment asks the viewmodel to retrieve some data from a Web site. That may take hundreds of milliseconds or longer, so the actual network I/O needs to be done on a background thread. When that I/O completes, the now-current fragment needs to find out about that, bearing in mind that it might be a different fragment instance by now, if a configuration change occurred while the I/O was ongoing.

The Jetpack solution to this problem is LiveData.

LiveData is itself a state container, designed to be held onto by a ViewModel. It has an Observer system, where interested parties can find out about changes in the data associated with the LiveData. So, if a background thread pushes new data into the LiveData, the LiveData will inform all of its observers. It does so on the main application thread, so observers do not need to worry about switching threads.

More importantly, LiveData is aware of lifecycles, such as those from activities and fragments. When an activity or fragment registers an Observer, and later the activity or fragment is destroyed, the LiveData automatically removes the associated Observer. Hence, when we undergo a configuration change, we do not need to remember to remove any Observer objects that we may have registered with LiveData — those get cleaned up automatically.

LiveData also automatically pushes an existing value, if there is one, to any new registered Observer objects. When coupled with the automatic-removal feature, this makes LiveData very convenient for dealing with configuration changes. The activities and fragments can largely forget about configuration changes entirely, just reacting to whatever the LiveData pushes to it. Those “pushes” will either be:

Usually, the activity or fragment does not care about which of those scenarios occurred and can handle them identically.

Sources of LiveData

In many cases, you will get LiveData objects handed to you from something else. For example, if you use Room for working with on-device databases, you can have the results of your queries be in the form of LiveData. We will explore that more later in the book.

Sometimes, you will use adapters to convert one form of reactive response into LiveData. For example, RxJava is the most popular reactive framework for Java. Jetpack offers a library that helps you convert RxJava reactive responses into LiveData, as RxJava itself knows nothing about Android and things like activity/fragment lifecycles. Similarly, with Kotlin coroutines, there are adapters supplied by the Jetpack, to help consume suspend functions and Flow via LiveData.

Otherwise, you are on your own for creating LiveData objects. For that, there are two main approaches:

  1. Create a subclass of LiveData that handles the background threading and delivery of updates
  2. Use MutableLiveData (which does not require a subclass the way LiveData does) and have code outside of that object deal with data sources, threading, etc.

Active and Inactive States

If a LiveData was instantiated in a forest, and nobody was there to observe data changes, does the LiveData really exist?

The answer is: yes, but it hopefully is not consuming any resources.

A LiveData implementation will be called with onActive() when it receives its first active observer. Here, “active” means that the activity or fragment registering the observer is in the started or resumed state. Conversely, the LiveData will be called with onInactive() once it no longer has any active observers, either because all observers have been unregistered or none of them are active, as their lifecycles are all stopped or destroyed.

The idea is that a LiveData would only start consuming significant system resources — such as requesting GPS fixes — when there are active observers, releasing those resources when there are no more active observers. This works in many cases, though there are some that will require more finesse. For example, given that the GPS radio takes some time before it starts generating GPS fixes, a LiveData for GPS might want to wait some amount of time after losing its last active observer before releasing the GPS radio, in case a new observer pops up quickly, to avoid delays in getting those GPS fixes.

Subclasses of LiveData, therefore, should override onActive() (and perhaps onInactive()) and use those events to control resource consumption.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.