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!