Preface

In this article we will dive deep into LiveData component. In case you missed the previous article or don’t know about architecture components then head on to my previous article Introduction to Android Architecture Components.

The code in this article is written in Kotlin. In case you don’t know Kotlin. Read this series of article to get started.

Introduction

LiveData is a component which holds the value (data holder) and allows to observe for changes in the value. It is designed to hold the data in ViewModel but it can also be used at other places in your application.

It uses Observable pattern to detect changes in the data. It’s pretty similar to RxJava observable with the main difference been, LiveData is lifecycle aware. We will at what lifecycle aware means later.

The main benefit is the fact that our UI components like RecycleView, observe LiveData for changes in the data and LiveData being lifecycle aware it knows about lifecycle of an Activity or Fragment.

In very simple terms, LiveData is just plain data type which has capability to notify it’s observers when data is changed.

Adding it to your project

Add the following dependencies in your app or module build.gradle

// For Lifecycle, ViewModel and LiveData
compile "android.arch.lifecycle:runtime:1.0.0-alpha9"
compile "android.arch.lifecycle:extensions:1.0.0-alpha9"
// Java
annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha9"
// Kotlin
kapt "android.arch.lifecycle:compiler:1.0.0-alpha9"

Using LiveData

A simple ViewModel class with the data being LiveData

User ViewModel with LiveData

We have just wrapped our data i.e List to list of live data i.e LiveData<List> That’s it! Now we just need to use it to observe for changes on our UI.

Usage

Observing the users in the UI

In our UI Activity or Fragment we will get the LiveData instance and observe for changes the data and update state of the UI.

For example, we can update the RecyclerView when data changes maybe due to adding new item or removing an item.

This is all simple usage of LiveData which you’ll mostly deal with.

UI — LiveData — LifecycleOwners

The UI components like RecyclerView observe changes on the LiveData and update themselves when the live data is changed.

Now you might be worried what happens when the UI is gone, for example if the users pressed home button and maybe after that the data is changed maybe by some other thread and you receive the observe call back. It’s definitely going to crash cause the UI is already gone.

Oh my god reaction!

Don’t worry the app is not going to crash. Fortunately the Android Team were aware of this hence they made LiveData lifecycle aware which means LiveData is aware of the state of it’s owners which in this case is Activity or Fragment

That’s great!

That’s great! But how does LiveData do this?

The observe() function called from our UI passes the LifecycleOwner as the first argument. It denotes that the observer passed as 2nd argument should be bound to that Lifecycle.

It gives the provides the following benefits:

  • If the Lifecycle is not in an active state (STARTED or RESUMED), the observer isn't called even if the value changes.
  • If the Lifecycle is destroyed, the observer is removed automatically.

Transformations of LiveData

Sometimes, you may want to make changes to the LiveData value before dispatching it to the observers, or you may need to return a different LiveData instance based on the value of another one.

For example, fetching data based upon the id in the details page

For these tasks the library provides different transformations. There is also provision to write custom transformations using MediatorLiveData , we will look at them later.

Using Transformations.switchMap()

Let’s take an interesting scenario to understand use of switchMap() transformation. Let’s say you have list of users and when you click on a particular user, a new activity is shown with details of the selected user.

UserViewModel with switchMap() transformation

Each time the userId is changed the switchMap() transformation is called and user instance is updated with new value based upon the userId .

Detail user activity showing details of selected user

When the detail activity is open, the user id of the selected user is notified to UserViewModel which automatically calls our transformation and the observe is called which updates the UI with the user details.

Technically speaking, the switchMap transformation takes a LiveData, makes the changes, unwraps the value and notifies the observer.

Using Transformations.map()

It is similar to switchMap transformation except in this, it takes the LiveData, makes the changes and notifies the observer.

UserViewModel with map() transformation

In a nutshell,

— use map() when you may want to make changes to the LiveData value before dispatching it to the observers and use

— switchMap() when you may need to return a different LiveData instance based on the value of another one.

Custom transformations

There might be cases when the default transformations won’t be applicable or sufficient to your needs, you’ll need a custom transformation. To implement your own transformation you can use the MediatorLiveData class.

It is used to listen to other LiveData instances and process events emitted by them. Moreover it takes cares to correctly propagate its active/inactive states to the source LiveData.

Interesting fact: The default transformations internally use MediatorLiveData to make the transformation possible

Writing your own LiveData class

Let’s say you wanted to read a file from storage and want to observe changes on the file then simply using LiveData won’t help, you’ll need to write your own by extending the LiveData class.

LiveData is a lifecycle aware component hence for custom LiveData class, we need to handle the behavior when the LifecycleOwner (Activity and Fragment) are present or not, it is done using the onActive() and onInactive() functions.

There are 3 important functions while extending LiveData class

  • onActive() — It is called when the LiveData has an active observer that means the observer’s lifecycle is in STARTED or RESUMED state. This means we need to start observing the location updates from the device.
  • onInactive() — This method is called when the LiveData does not have any active observers. Since no observers are listening, there is no reason to observe for changes on the data.
  • setValue() — Calling this method updates the value of the LiveData instance and notifies active observers about the change.

Following is a custom LiveData class which observes for changes in the file and notifies on any change in the file content. We use FileObserver API to observe for file changes.

We can use the onActive() and onInactive() functions to only listen when there’s an active observer on our data — as long as someone is observing, they can be guaranteed to get the latest data.

The onEvent() callback is triggered when the file content changes and we read the complete file and set the new data using setValue() function which notifies of data change to the active observer.

Conclusion

LiveData is a lifecycle aware component useful to keep our data up-to date which in turn keeps our UI up-to date, even if the UI is re-created due to configuration change. It prevents crashes caused due to destroyed Activity or Fragment.

LiveData with ViewModel, double the advantages of LiveData and make it easier to separate data from UI while keeping the UI up-to date.

Overall, I really like how LiveData component is designed to help us build apps with the reactive nature of Android.

--

--

Akshay Chordiya
AndroidPub

Google Developer Expert @ Android | Android Engineer @ Clue | Instructor @Caster.IO