Android LiveData: Handle UI Event With Event Wrapper (SingleLiveEvent Replacement)

August 19, 2019

Why not to use LiveData to handle UI event? Because LiveData will trigger again upon configuation change / screen rotation. We want to handle UI event such as click once only.

The above problem could be solved using SingleLiveEvent.

Alternatively, you can use Event wrapper.

open class Event<out T>(private val value: T) {
    private val pending = AtomicBoolean(true)

    fun getIfPending(): T? {
        return if (pending.compareAndSet(true, false)) {
            value
        }
        else {
            null
        }
    }

    fun peek(): T = value

    fun isPending(): Boolean = pending.get()
}

NOTE: Based on Event by JoseAlcerreca

NOTE: Solution for multiple observers (I didn’t try this)

Usage

val selectItemEvent = MutableLiveData<Event<Int>>()
// trigger ui event on item selected
selectItemEvent.value = Event(10)
selectItemEvent.observe(lifecycleOwner, Observer { event ->
    val position = event.getIfPending()
    if (position != null) {
        // do something
    }
})

To reduce boilerplate code, you can use an EventObserver.

class EventObserver<T>(private val observe: (T) -> Unit) : Observer<Event<T>> {
    override fun onChanged(event: Event<T>?) {
        event?.getIfPending()?.let { value ->
            observe(value)
        }
    }
}

NOTE: Based on EventObserver by JoseAlcerreca

selectItemEvent.observe(lifecycleOwner, EventObserver { position ->
    // do something
})
This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.