NonNull LiveData with Kotlin extension

Henry Tao
ProAndroidDev
Published in
2 min readApr 20, 2018

--

If you are currently using Google Android Architecture Components, you probably know what LiveData is. If not, you can check it out at https://developer.android.com/topic/libraries/architecture/index.html.

In this post, we are going to discuss a few ways to make LiveData a lot easier to use, especially making it NonNull safe. We also use Kotlin instead of Java (Sorry Java).

The common way to use NonNull LiveData

Let’s see this example:

val fooLiveData: MutableLiveData<Boolean> = MutableLiveData()
fooLiveData.observe(this, Observer {
it
?.let {
// Now foo is non-null
}
}
)

Checking it? is not so bad, but it would be nicer if we can some how filter null value. One solution can be making NonNullObserver<T> by extending Observer<T>.

abstract class NonNullObserver<T> : Observer<T> {

abstract fun onNonNullChanged(t: T)

override fun onChanged(t: T?) {
t?.let { onNonNullChanged(it) }
}
}
// Usage
fooLiveData.observe(this, object : NonNullObserver<Boolean>() {

override fun onNonNullChanged(t: Boolean) {
// Now foo is non-null
}
})

Although it solves the problem, the usage is really ugly.

Solution 1: create nonNullObserve Kotlin extension

fun <T> LiveData<T>.nonNullObserve(owner: LifecycleOwner, observer: (t: T) -> Unit) {
this.observe(owner, android.arch.lifecycle.Observer {
it
?.let(observer)
})
}
// Usage
fooLiveData.nonNullObserve(this, {
// Now foo is non-null
})

The usage is nicer, but you need to define new method to differentiate between normal observe and non-null observe. There is a better solution for it.

Solution 2 (best): create nonNull Kotlin extension and NonNullMediatorLiveData<T>

Firstly, we define NonNullMediatorLiveData<T> extending from MediatorLiveData<T>. The purpose of it is that we can create extension function for NonNullMediatorLiveData returned from .nonNull . If you are not familiar with MediatorLiveData, you can check it out at https://developer.android.com/reference/android/arch/lifecycle/MediatorLiveData.html

class NonNullMediatorLiveData<T> : MediatorLiveData<T>()

Secondly, we create Kotlin extension

fun <T> LiveData<T>.nonNull(): NonNullMediatorLiveData<T> {
val mediator: NonNullMediatorLiveData<T> = NonNullMediatorLiveData()
mediator.addSource(this, Observer { it?.let { mediator.value = it } })
return mediator
}
fun <T> NonNullMediatorLiveData<T>.observe(owner: LifecycleOwner, observer: (t: T) -> Unit) {
this.observe(owner, android.arch.lifecycle.Observer {
it
?.let(observer)
})
}
// Usage
fooLiveData
.observe(this, Observer {
// foo is still nullable
})
fooLiveData
.nonNull()
.observe(this, {
// Now foo is non-null
})

Bravo! As you can see, there is slight change in how you observe and we have very nice and safe way to observe non-null LiveData. This solution does not restrict you to set null value in the source LiveData. Therefore, you still can get nullable value when you call fooLiveData.value but it makes sense.

Let me know your thought and happy coding!

--

--

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