When LiveData and Kotlin don’t play well together

Danny Preussler
Dec 15, 2020 · 4 min read
Playing well together

The idea of LiveData was an interesting one. Based on the idea of reactive streams, that was on the peak at that time with RxJava plus adding automatic lifecycle handling — a problem on Android. LiveData had bad timing though. It arrived just before Kotlin made its impact in the Android community and sometimes both don't play that nice together. Let’s explore why and what can happen!

The good and the bad

This is one of the core features of LiveData. But very soon after developers, including the authors, figured, you don’t always want that behavior. Let’s say you have an error value. Then you probably don’t want to show that error value again after resubscribing, like after rotation of a device — for instance.

One way to solve this is SingleLiveEvent and it’s a good choice for one-off events like errors.

Both worlds

Let’s look at the implementation ofLiveData:

The current value is saved on a field:

private volatile Object mData;

Initially, it is set to NOT_SET which.

static final Object NOT_SET = new Object();

Unfortunately, is not public otherwise, we could use it to reset the value.

What now?

Let’s explore this:

viewModel.results.observe(lifecycleOwner) { result ->
when (result) {
SomeResult.Error -> {
handleError()
viewModel.results.reset()
}
SomeResult.Result -> { handleResult(result) }
}
}

where the ViewModel’s implementation is:

class MyViewModel: ViewModel() {

private val mutableResults = MutableLiveData<SomeResult>()

val results: LiveData<SomeResult>
get() = mutableResults

fun reset() {
mutableResults.value = null
}

}

This works, right?

Congratulations! You just introduced a crash into your code!

But why?

The issue here is that we declared LiveData as non-nullable to the compiler!

Your observer will get notified with this null value and then immediately crash once it gets a value with a KotlinNullPointerException under the hood. There is nothing you can do anymore! It’s part of how Kotlin works!

LiveData is written in Java. It does allow setting it’s value to null. So the authors could add @Nullable annotation as we have them on other Jetpack libraries, right? No, as then you always need to handle nullable types in getValue or the Observer although you clearly might have declared your LiveData as non-null. The Generic is runtime information (for Java), annotations are compile-time information, they can’t solve this for us. And Java simply does not have null in the type system.

It’s not a bug - it’s a feature!

How can you solve this?

private val mutableResults = MutableLiveData<SomeResult?>()

val results: LiveData<SomeResult?>
get() = mutableResults

Now we can handle that in the observer and only react to non null values. And more important: the compiler forces us to!

Is there another way?

package androidx.lifecycle

fun <T> LiveData<T>.reset(){
this.value = LiveData.NOT_SET as T?
}

It’s a bit dirty though.

But other than that, is this actually allowed?
It’s at least not forbidden! Even by the newer stricter rules about private APIs for apps on Google Play, it should be fine.

The actual problem is a different one, but most importantly, you rely on an implementation detail of LiveData that is not part of the public API and therefore could change anytime!

Ok, what now?

sealed class ViewModelResult {
data class Result(val result: SomeResult): ViewModelResult()
object NotSet: ViewModelResult()
}

We would use NotSet to clear of previous values and then could just ignore that in the Observer.

It’s not the most beautiful but probably the cleanest way of solving this.
This is also recommended by Jose Alcérreca in this article.

Anything else?

If you want to stick with LiveData, think if there is a way you can either have sticky or non sticky events instead of a mix?

In that case, think about updating your Linter to add a custom Lint check where our non-nullable live data cannot be set to null and the compiler in that case would cause an error.

Stay alert! This is not a LiveData issue, it could happen with other libraries too. We got very used to the Kotlin compiler warning us. But if it can’t, it might bring you into troubles you won’t even have without it.

Google Developers Experts

Experts on various Google products talking tech.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store