Data and state having fun together (Neko Tai @ Unspash)

LiveData is a great way to send your data updates to the UI, but as commented in the Guide to app architecture in the final lines, dealing with loading state and handling errors are not trivial.

Consider de following scenario: when opening your activity, you want to display the list of items you have previously saved in local storage (if any) and display some kind of loading indicator while you go to the server for the fresh data. Once the data is ready, hide the loading and update the data in UI to be the latest. But let’s add something else, error handling. You want to display an error and retry button, but maybe only when there is no data in DB.

Overview

We are gonna go for the classic approach of ViewModel + Repository + Room, being the repository the responsible from requesting data to both server and Room. Nothing different that you have already seen in the Android docs.

A common way to handle loading/error state is to use two separated LiveData, one for the List of items and another one for the state. This last one could be just an Int representing LOADING, ERROR and SUCCESS.

The only problem is that you have two LiveData, wouldn’t be great to have only one? MediatorLiveData for the win.

Using MediatorLiveData

This kind of LiveData hear from any other LiveData declared as source, so any change to any of the sources will trigger a MediatorLiveData change. We are gonna use this to return on single item to the UI.

First, to represent the state we will use a class Resource.

Out repository will hold the magic, creating the two LiveData, one coming from Room and another one created to maintain state, but will merge both using MediatorLiveData.

When getItems() is called, whatever it is in db will be returned as a success, indicating the UI that the data should be displayed. Before doing the request, a LOADING status is sent to the status liveData triggering the MediatorLiveData, which will display the loading in the UI.

Then the request is done and, if everything goes well, data is inserted in db, triggering a new update in MediatorLiveData, propagating SUCCESS, which will be used by the view to remove the loading. A similar way to the loading is used to communicate error.

Let’s take a look to the code of the Activity and de ViewModel.

And that’s it, data and state are communicated to the UI together. In case you want to take a look to the full code, it is here.

Happy coding!