Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to emit distinct values from MutableLiveData?

I observed that MutableLiveData triggers onChanged of an observer even if the same object instance is provided to its setValue method.

//Fragment#onCreateView - scenario1
val newValue = "newValue"
mutableLiveData.setValue(newValue) //triggers observer
mutableLiveData.setValue(newValue) //triggers observer

//Fragment#onCreateView - scenario2
val newValue = "newValue"
mutableLiveData.postValue(newValue) //triggers observer
mutableLiveData.postValue(newValue) //does not trigger observer

Is there a way to avoid an observer be notified twice if the same or an equivalent instance is provided to setValue()/postValue()

I tried extending MutableLiveData but that did not work. I could be missing something here

class DistinctLiveData<T> : MutableLiveData<T>() {

    private var cached: T? = null

    @Synchronized override fun setValue(value: T) {
        if(value != cached) {
            cached = value
            super.setValue(value)
        }
    }

    @Synchronized override fun postValue(value: T) {
        if(value != cached) {
            cached = value
            super.postValue(value)
        }
    }
}
like image 480
Pravin Sonawane Avatar asked Feb 11 '19 21:02

Pravin Sonawane


3 Answers

There is already in API : Transformations.distinctUntilChanged()

distinctUntilChanged

public static LiveData<X> distinctUntilChanged (LiveData<X> source)

Creates a new LiveData object does not emit a value until the source LiveData value has been changed. The value is considered changed if equals() yields false.

<<snip remainder>>

like image 102
Jurij Pitulja Avatar answered Nov 16 '22 12:11

Jurij Pitulja


You can use the following magic trick to consume "items being the same":

fun <T> LiveData<T>.distinctUntilChanged(): LiveData<T> = MediatorLiveData<T>().also { mediator ->
    mediator.addSource(this, object : Observer<T> {
        private var isInitialized = false
        private var previousValue: T? = null

        override fun onChanged(newValue: T?) {
            val wasInitialized = isInitialized
            if (!isInitialized) {
                isInitialized = true
            }
            if(!wasInitialized || newValue != previousValue) {
                previousValue = newValue
                mediator.postValue(newValue)
            }
        }
    })
}

If you want to check referential equality, it's !==.


But it has since been added to Transformations.distinctUntilChanged.

like image 13
EpicPandaForce Avatar answered Nov 16 '22 12:11

EpicPandaForce


If we talk about MutableLiveData, you can create a class and override setValue and then only call through super if new value != old value

class DistinctUntilChangedMutableLiveData<T> : MutableLiveData<T>() {
    override fun setValue(value: T?) {
        if (value != this.value) {
            super.setValue(value)
        }
    }
}
like image 3
Kuno Avatar answered Nov 16 '22 13:11

Kuno