Как выдавать разные значения из MutableLiveData?

Я заметил, что MutableLiveData запускает onChanged наблюдателя, даже если тот же экземпляр объекта предоставляется его setValue методу.

//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

Есть ли способ избежать двойного уведомления наблюдателя, если один и тот же или эквивалентный экземпляр предоставляется setValue()/postValue()

Я попытался расширить MutableLiveData, но это не сработало. Я мог что-то упустить здесь

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)
        }
    }
}

person Pravin Sonawane    schedule 11.02.2019    source источник


Ответы (4)


Вы можете использовать следующий фокус, чтобы потреблять одинаковые предметы:

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)
            }
        }
    })
}

Если вы хотите проверить ссылочное равенство, это !==.


Но с тех пор он был добавлен в Transformations.distinctUntilChanged.

person EpicPandaForce    schedule 11.02.2019

В API уже есть: Transformations.distinctUntilChanged()

различный без изменений

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

Создает новый объект LiveData и не выдает значение, пока исходное значение LiveData был изменен. Значение считается измененным, если equals() дает false.

<<отрезать остаток››

person Jurij Pitulja    schedule 24.03.2019
comment
Поскольку это было помечено как возможный ответ только по ссылке, я сделал выдержку из документации. - person dbc; 25.03.2019
comment
Стоит отметить, что Transformations.distingUntilChanged() доступна в версии 2.1.0, которая еще не является стабильной! developer.android.com/jetpack/androidx/releases/ - person ASP; 29.07.2019
comment
обновление: теперь оно находится в стабильной версии. безопасно использовать. - person Nino van Hooff; 30.03.2020

Если мы говорим о MutableLiveData, вы можете создать класс и переопределить setValue, а затем вызывать только через super, если new value != old value

class DistinctUntilChangedMutableLiveData<T> : MutableLiveData<T>() {
    override fun setValue(value: T?) {
        if (value != this.value) {
            super.setValue(value)
        }
    }
}
person Kuno    schedule 12.06.2019
comment
не уверен, почему это не получило больше голосов, кажется самым простым и чистым способом imo. - person bastami82; 18.09.2020
comment
Да, вы должны знать, как работает равенство, хотя это не делает ответ неправильным. - person Kuno; 28.09.2020
comment
@bastami82: Пожалуйста, перестаньте менять мой ответ. Исправьте мои опечатки, добавьте форматирование, это нормально, для всего остального напишите свой ответ. - person Kuno; 03.10.2020
comment
просто пытался быть полезным, так как мои предложенные изменения НЕ изменили сути вашего ответа, я мог бы написать свой собственный ответ, вы правы, но я подумал, что, принимая во внимание ваш ответ, к сожалению, вы не оценили усилия (кстати так развивается код). На мой взгляд, предложенные изменения сделали его более ясным, я только подумал, что это хороший ответ и нуждается в небольшой доработке, разочарован, что вам это не понравилось, но я уважаю ваше решение. - person bastami82; 05.10.2020

В моем случае у меня есть довольно сложные объекты, которые я должен сравнить по некоторым полям. Для этого я изменил ответ EpicPandaForce:

fun <T> LiveData<T>.distinctUntilChanged(compare: T?.(T?) -> Boolean = { this == it }): LiveData<T> = MediatorLiveData<T>().also { mediator ->
    mediator.addSource(this) { newValue ->
        if(!newValue.compare(value)) {
            mediator.postValue(newValue)
        }
    }
}

По умолчанию используется стандартный метод equals, но если вам нужно - вы можете изменить логику различения

person Andrey Turkovsky    schedule 04.10.2019