Chaining LiveData like RxJava with Kotlin extension

Henry Tao
Henry Tao
May 18, 2018 · 2 min read

If you haven’t read my article about NonNull LiveData, you need to read it. Same idea will be reused here.

If you don’t know about RxJava, you also need to read about it from https://github.com/ReactiveX/RxJava.

In this article, we will talk about how we can borrow some ideas from RxJava and apply them to LiveData. Of course, thanks to Kotlin extension. It helps developer life easier. Sorry Java! The goal will look like this:

val liveData: MutableLiveData<Boolean> = MutableLiveData()
liveData
.distinct()
.filter { it == false }
.map { true }
.nonNull()
.observe(lifecycleOwner, { result ->
// result is non-null and always true
})

The simplest approach

Let’s take distinct as an example.

val liveData: MutableLiveData<Boolean> = MutableLiveData()
liveData
.distinct()
.observe(lifecycleOwner, { result ->
// new result is different from previous result
})

In order to do as above, you need to create distinct and observe extension for LiveData.

fun <T> LiveData<T>.distinct(): LiveData<T> {
val mediatorLiveData: MediatorLiveData<T> = MediatorLiveData()
mediatorLiveData.addSource(this) {
if
(it != mediatorLiveData.value) {
mediatorLiveData.value = it
}
}
return
mediatorLiveData
}
fun <T> LiveData<T>.observe(owner: LifecycleOwner, observer: (t: T?) -> Unit) {
observe(owner, Observer { observer(it) })
}

You can follow the same pattern for other methods like filter, map

Supporting NonNull

Supporting NonNull is a bit tricky. As I mentioned in NonNull LiveData article, you need to have NonNullMediatorLiveData to differentiate between null and non-null observer. Therefore, you may need to duplicate you logic for each of extension method. Your filter extension will look like this:

fun <T> LiveData<T>.distinct(): LiveData<T> {
val mediatorLiveData: MediatorLiveData<T> = MediatorLiveData()
mediatorLiveData.addSource(this) {
if
(it != mediatorLiveData.value) {
mediatorLiveData.value = it
}
}
return
mediatorLiveData
}
// Exactly same logic but return NonNullMediatorLiveData
fun <T> NonNullMediatorLiveData<T>.distinct(): LiveData<T> {
val mediatorLiveData: NonNullMediatorLiveData<T> = NonNullMediatorLiveData()
mediatorLiveData.addSource(this) {
if
(it != mediatorLiveData.value) {
mediatorLiveData.value = it
}
}
return
mediatorLiveData
}
fun <T> LiveData<T>.observe(owner: LifecycleOwner, observer: (t: T?) -> Unit) {
observe(owner, Observer { observer(it) })
}

Now you can do distinct and nonNull together despite the order.

val liveData: MutableLiveData<Boolean> = MutableLiveData()
liveData
.nonNull()
.distinct()
.observe(lifecycleOwner, { result ->
// new result is different from previous result
})
// or
liveData
.distinct()
.nonNull()
.observe(lifecycleOwner, { result ->
// new result is different from previous result
})

Polishing code with MediatorObserver

Duplicating logic is not nice. We can improve it by defining interface MediatorObserver<IN, OUT>. You code will look cleaner like this:

private class DistinctExt<T> : MediatorObserver<T, T> {

override fun run(source: LiveData<T>, mediator: MediatorLiveData<T>, value: T?) {
if (value != mediator.value) {
mediator.value = value
}
}
}

fun <T> LiveData<T>.distinct(): LiveData<T> = createMediator(this, DistinctExt())
fun <T> NonNullMediatorLiveData<T>.distinct(): NonNullMediatorLiveData<T> = createMediator(this, DistinctExt())

For more information, take a look at LiveData.kt.

Ready to use library

You can try out https://github.com/henrytao-me/livedata-ktx. Adding one line of code in your build.gradle and ready to rock:

implementation "me.henrytao:livedata-ktx:LATEST_VERSION"

Feel missing methods?

Please suggest what you need by creating issues. I will support it as fast as I can.

Let me know your thought and happy coding!

Google Developers Experts

Experts on various Google products talking tech.

Henry Tao

Written by

Henry Tao

Code-Eat-Code, Android, Node, ReactNative, Google Developer Expert in Android, Android@Shopify

Google Developers Experts

Experts on various Google products talking tech.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade