Android 如何避免MediatorLiveData的所有源调用同一个函数?

Android 如何避免MediatorLiveData的所有源调用同一个函数?,android,android-livedata,android-jetpack,event-driven,Android,Android Livedata,Android Jetpack,Event Driven,问题介绍: 当使用android随android jetpack添加的MediatorLiveData时,我发现自己经常从每个源调用相同的函数。例如,这可能是因为,每当更新一个源时,我必须检查它是否有影响,或者另一个源是否更重要。代码中的示例(Kotlin,但不重要): val isHovered=MutableLiveData() val isSelected=MutableLiveData() val color=MediatorLiveData()。应用{ addSource(isHove

问题介绍: 当使用android随android jetpack添加的
MediatorLiveData
时,我发现自己经常从每个源调用相同的函数。例如,这可能是因为,每当更新一个源时,我必须检查它是否有影响,或者另一个源是否更重要。代码中的示例(Kotlin,但不重要):

val isHovered=MutableLiveData()
val isSelected=MutableLiveData()
val color=MediatorLiveData()。应用{
addSource(isHovered){updateColor()}
addSource(isSelected){updateColor()}
}
有趣的更新颜色(){
如果(isHovered.value==true)
color.value=color.GREEN
else if(isSelected.value==true)
color.value=color.RED
其他的
color.value=color.GRAY
}
悬停时该项目为绿色,选中且未悬停时为红色,否则为灰色。当isSelected更改为true时,我仍然需要检查它是否悬停,然后再将颜色更改为红色。此外,当iOvering更改为false时,我需要在将颜色更改为灰色之前检查是否选中它。因此,最简单的是一个函数,它将所有变量都考虑在内,并相应地设置颜色

我的问题:
当MediaLiveData从非活动变为活动时,由于视图被移动到前台,可能会对每个更改的源多次调用函数
updateColor
。这是不必要的,因为每个调用已经考虑了所有变量。由于此函数可能非常复杂,并且可能有许多源,有没有办法避免对源LiveData的相同状态多次调用它?

要避免多次调用,您应该只从
LiveData
实例发送新的更新。要实现这一点,可以使用
SingleLiveEvent

一个生命周期感知的可观察对象,在 订阅,用于导航和Snackbar消息等事件

这避免了事件的一个常见问题:配置更改 (如旋转)如果观察者处于活动状态,则可以发出更新。这 LiveData仅在显式调用 setValue()或call()

请注意,只有一个观察者会收到更改通知


您可能无法阻止
IsOvered
isSelected
发射项目,但您可以阻止
color
发射多个相同类型的项目。其思想是仅当当前颜色不等于新颜色时才更改颜色

fun updateColor() {
    var newColor: Color?    

    if (isHovered.value == true)
        newColor = Color.GREEN
    else if (isSelected.value == true)
        newColor = Color.RED
    else
        newColor = Color.GRAY

    if (color.value != newColor) {
        color.value = newColor!!
    }
}

我也遇到了同样的问题,并提出了以下解决方案。首先,将所有来源的值收集到一个值:

data class State(val isHovered: Boolean, val isSelected: Boolean)

private val state = MediatorLiveData<State>().apply {
    fun update() {
        value = State(isHovered.value ?: false, isSelected.value ?: false)
    }

    addSource(isHovered) { update() }
    addSource(isSelected) { update() }
}
通常,当更改
LiveData
值时,它会立即通知同一堆栈帧中的所有观察者。假设您有一个
中介livedata
,并更改多个源的值,如:

isHovered.value = true
isSelected.value = true

状态
值将在一行中更改两次
状态(真、假)
状态(真、真)
。它们都将被分派,即使使用上面的
distinct
,因为值实际上是不同的
squashUpdates
在这种情况下有助于将调度延迟到堆栈帧的末尾,仅调度最后一个值

SingleLiveEvent是我正在使用的一种模式,但是如果所有源LiveData都有新的值,它就不起作用。公共函数仍然会被频繁调用。我明白了。我已经有了一个助手函数,它只在实际更改时更新。但这并不能阻止昂贵的函数运行两次为什么需要
apply
函数?@IgorGanapolsky这是一个Kotlin的东西,允许我直接使用对象,同时返回它。您也可以从构造函数中调用
addSource
函数。
fun <T> LiveData<T>.distinct(): LiveData<T> = MediatorLiveData<T>().apply {
    var wasSet = false
    addSource(this@distinct) {
        // Update value if it has been changed or it's the first time the method called
        // Because value is null by default it will not be set to null again, leaving the live data in [value not set] state
        if (value != it || !wasSet) {
            value = it
            wasSet = true
        }
    }
}
fun <T> LiveData<T>.squashUpdates() = MediatorLiveData<T>().apply {
    addSource(this@squashUpdates) {
        postValue(it)
    }
}
isHovered.value = true
isSelected.value = true