如何从MutableLiveData中发出不同的值?

20
我注意到即使将相同的对象实例提供给其setValue方法,MutableLiveData仍会触发观察者的onChanged方法。
//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)
        }
    }
}
5个回答

46

API中已经有了:Transformations.distinctUntilChanged()

distinctUntilChanged

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

创建一个新的LiveData对象,直到源LiveData值发生变化之前不会发出任何值。如果equals()返回false,则该值被视为已更改。

<<snip remainder>>


2
由于这个回答被标记为可能只包含链接,我摘录了一部分文档内容。 - dbc
2
值得一提的是,Transformations.distingUntilChanged() 方法最早在 Ver 2.1.0 中可用,但该版本尚未稳定发布! https://developer.android.com/jetpack/androidx/releases/lifecycle#2.1.0-alpha01 - ASP
1
更新:现在已经是稳定版本,可以放心使用。 - Nino van Hooff

14
您可以使用以下魔术技巧来消耗“相同的项目”:
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中。


3
如果我们谈论MutableLiveData,你可以创建一个类并覆盖setValue方法,然后只有在"新值 != 旧值"时才通过super进行调用。
class DistinctUntilChangedMutableLiveData<T> : MutableLiveData<T>() {
    override fun setValue(value: T?) {
        if (value != this.value) {
            super.setValue(value)
        }
    }
}

1
不确定为什么这个没有得到更多的赞,我认为这是最简单和最干净的方法。 - bastami82
是的,你应该知道相等性如何工作,但这并不意味着答案是错误的。 - Kuno
1
@bastami82:请停止更改我的答案。纠正我的拼写错误,添加一些格式,那没问题,但是对于其他任何内容,请编写您自己的答案。 - Kuno
2
我只是想尽力帮忙,因为我的建议并没有改变你答案的本质。我当然可以写自己的答案,但我认为应该把所有的功劳归于你的答案。可惜你并没有欣赏我的努力(顺便说一句,这就是代码进化的方式)。在我看来,我的建议使答案更清晰了。我只是认为这是一个好答案,需要一些润色。很遗憾你不喜欢,但我尊重你的决定。 - bastami82

1
在我的情况下,我有一些相当复杂的对象,需要通过某些字段进行比较。为此,我已经修改了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方法,但如果需要,您可以更改区分逻辑。

0
我不使用MediatorLiveData,只是包装我的观察者,并记录和比较它。在我的livedata-Bus中运行良好。
class DiffObserver<X>(var observer: Observer<X>) : Observer<X>{

    var mFirstTime = true
    var previousValue : X? = null

    override fun onChanged(currentValue: X?) {
        if (mFirstTime || (previousValue == null && currentValue != null) ||
                (previousValue != null && currentValue == null) ||
                previousValue?.equals(currentValue) != true) {
            mFirstTime = false
            observer.onChanged(currentValue)
        } 
        previousValue = currentValue
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接