How I made my own LiveData!

Hitanshu Dhawan
AndroIDIOTS
Published in
5 min readMay 4, 2019

--

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.

When I first used LiveData, I was just amazed by it.
Now I can get a callback whenever anyone changes the LiveData.
Also, the callback will only be received only if the UI is in an active state.

This is extremely useful for an app that shows UI based on a data model.
Using LiveData, I can observe the data model and update the UI whenever its value changes.

I was so amazed by using LiveData that I wanted to know all the magic behind it. By doing some googling, I found that it follows a simple design pattern knowns as the Observer Pattern.

So, basically…

LiveData = Observer Pattern + Lifecycle Awareness

All this was so interesting that I decided to make my very own LiveData.
In this article, I'll show you how I made my own LiveData, what was my approach, and what were my learnings.

LiveData<T>

Let's start with the data holder part first.

Here's a basic implementation of LiveData that will hold/wrap our data.

Make it Observable!

Now, it's time to make our LiveData observable.

This is an official definition of the Observer Pattern from Wikipedia.

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

According to this definition, our LiveData is the subject.
So our LiveData should maintain a list of observers and all these observers should be notified whenever the value of our LiveData changes.

Here's the implementation of LiveData that is observable.

As you can see, now our LiveData contains an ArrayList of the observers that are attached to it.

val mObservers = ArrayList<(T?) -> Unit>()

Here, (T?) -> Unit is a function type which denotes that our observers must be lambda expressions which take one argument T? and returns Unit.

For example, if we make a LiveData of an integer like this…

val liveData = LiveData<Int>()

Then our observer's lambda expression would be like this…

{ data: Int? ->
// do something
}

To add and remove these observers from the list we have two methods observe and removeObserver respectively. Both of these methods take the observer as an argument of function type (T?) -> Unit.

The main magic is in the setValue method.

fun setValue(value: T) {
mValue = value

for (observer in mObservers) {
observer.invoke(mValue)
}

}

Here, after setting the value we are looping through all the observers that are attached and invoking them with the updated value. This way all the observers will receive the updated value and they can handle the new value appropriately.

Now, our LiveData is ready to be observed.

This is how we can use it in our apps…

val liveData = LiveData<Int>()

liveData.observe { data ->
// do something
}

But! This version of LiveData has some problems.

  1. The observers will receive the updated value even when the UI is in an inactive state.
  2. We will have to explicitly remove the observers from our LiveData when the UI is destroyed. They will not be removed automatically.
  3. The observers will not receive the value immediately after it is attached to the LiveData. The value will only be received when someone sets the value of LiveData using a setValue method.

Basically, our LiveData is not lifecycle aware.

Make it Aware! Lifecycle Aware!

Now, it’s time to make our LiveData lifecycle aware.

In order to make our LiveData lifecycle aware we will take help of the Lifecycle-Aware Components in which the android.arch.lifecycle package contains all the classes and interfaces that will help us.

Have you noticed when using the real LiveData we have to pass two arguments to it like this…

liveData.observe(this, Observer {
// do something
})

The real LiveData’s observe method also accepts a LifecycleOwner in addition to the observer. This is what makes it lifecycle aware.

The LifecycleOwner is an interface that is implemented by components that have a Lifecycle. Fragment and FragmentActivity classes implement this LifecycleOwner interface, and we can access their lifecycle with the getLifecycle method.

So by passing the LifecycleOwner to our LiveData, we can easily know and observe the lifecycle of the observer.

Here’s the implementation of LiveData which accepts LifecycleOwner and only notifies the observers only if they are in an active state.

As you can see, now our LiveData contains a HashMap of observers and owners associated with them.

val mObservers: HashMap<(T?) -> Unit, LifecycleOwner> = HashMap()

Also when looping through all the observers, we are checking its owner's current state and invoking them only if they are in an active state.

fun setValue(value: T) {
mValue = value

for ((observer, owner) in mObservers) {
if (owner.lifecycle.currentState.isAtLeast(STARTED))
observer.invoke(mValue)
}

}

Now, our observers will receive the updated value only if they are in an active state.

But! What about removing observers from the LiveData when their UI components are destroyed? And what about passing the current value to the observers immediately after they are attached to the LiveData?

For that, we need to monitor the lifecycle associated with the observers.

Here comes the LifecycleObserver.
As the name suggests it's used to monitor the lifecycle of a LifecycleOwner.

class MyObserver : LifecycleObserver {

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun connectListener() {
// ...
}

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun disconnectListener() {
// ...
}
}

This is how we can implement a LifecycleObserver.
The methods annotated with @OnLifecycleEvent will be executed once their respective lifecycle events are dispatched.

Then this LifecycleObserver can be attached to a LifecycleOwner like this…

owner.lifecycle.addObserver(MyObserver())

You can know more about all these Lifecycle-Aware Components here.

Here’s the implementation of LiveData which is lifecycle aware.

As you can see, now the observe method is initializing and attaching the LiveDataLifecycleObserver to the owner.

owner.lifecycle.addObserver(lifecycleObserver)

And, the LiveDataLifecycleObserver is having 3 annotated methods.

private inner class LiveDataLifecycleObserver(
val owner: LifecycleOwner, val observer: (T?) -> Unit)
: LifecycleObserver {

@OnLifecycleEvent(Lifecycle.Event.ON_START)
private fun onStarted() {
notifyChange(this)
}

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
private fun onResumed() {
notifyChange(this)
}

@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
private fun onDestroyed() {
removeObserver(observer)
}

}

onStarted and onResumed will notify the observer with the updated value whenever the UI component comes to an active state.

onDestroyed will remove the observer from the LiveData once the UI component is destroyed.

Now, our LiveData is finally complete and ready to use!

Making my own LiveData was a fun and interesting exercise.
I hope this article helped you in understanding the internals of LiveData.

The full source code can be found here!

Thanks for reading! Share this article if you found it useful.
Please do Clap 👏 to show some love :)

Let’s become friends on LinkedIn, GitHub, Facebook, Twitter.

--

--

Hitanshu Dhawan
AndroIDIOTS

Senior Software Engineer @ Urban Company | Google Certified Android Developer