Sapan Diwakar

Software developer

Follow me on Twitter Check out my code on GitHub View some of my designs on Dribbble Take a look at my Linked In profile

Historical Live Data for Android

Let's begin with a quick glance from the docs for LiveData

LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.

So why should you use it?

  • It ensures the UI always remains in sync with the data
  • It automatically cleans up after itself based on the lifecycle object used. So no callbacks lying around after your Activity or Fragment is finished.
  • No more checking for active activity/fragment from the callbacks.

The most basic LiveData subclasses that you can use straight away are MutableLiveData to publish your own data based on certain events from the app/framework you are using and MediatorLiveData to compute live data based off from other LiveData instances. While both of these deserve a tutorial of their own, I will leave them off for another post since they are already pretty thoroughly covered over the internet.

Let's jump on how to create a custom Live Data instance. Here we will try to create a HistoricalLiveData instance that hooks on to another LiveData and stores a history of n values from that data. Any bells ringing on how this could be useful for you?

Here's the full implementation

class HistoricalLiveData<T> constructor(source: LiveData<T>, private val maxLength: Int = 10) :  
    MediatorLiveData<List<T>>() {

    init {
        this.addSource(source) {
            val values = (this.value ?: emptyList()) + listOf<T>(it)
            this.postValue(values.subList(listOf(0, values.size - maxLength).max() ?: 0, values.size))
        }
    }
}

That's it. Isn't it pretty? I think it's almost all self-explanatory. But let's take a look anyway. We inherit from the MediatorLiveData class which handles listening for the source LiveData coming in as an argument very easy in the init. On the addSource callback, we add the value to our value and MediatorLiveData will take care of everything from there on.

Time to use it now.

private val historicalLocation = HistoricalLiveData<Location>(throttledLocation, 2)  
...
historicalLocation.observe(lifecycleOwner, Observer {  
  Timber.d("Previous 2 locations: %s", it.joinToString(",")) 
})