如何避免调用相同函数的 MediatorLiveData 的所有来源?

fin*_*usl 2 android event-driven android-livedata android-jetpack

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

val isHovered = MutableLiveData<Boolean>()
val isSelected = MutableLiveData<Boolean>()

val color = MediatorLiveData<Int>().apply { 
    addSource(isHovered) { updateColor() }
    addSource(isSelected) { updateColor() }
}

fun updateColor() {
    if (isHovered.value == true)
        color.value = Color.GREEN
    else if (isSelected.value == true)
        color.value = Color.RED
    else
        color.value = Color.GRAY
}
Run Code Online (Sandbox Code Playgroud)

悬停时项目为绿色,选中时为红色,未悬停时为红色,否则为灰色。当 isSelected 变为 true 时,在将颜色更改为红色之前,我还需要检查它是否处于悬停状态。此外,当 isHovering 更改为 false 时,我需要在将颜色更改为灰色之前检查它是否被选中。所以最简单的是一个函数,它考虑了所有变量并相应地设置颜色。

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

ese*_*sov 5

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

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() }
}
Run Code Online (Sandbox Code Playgroud)

然后我创建了一个扩展函数来只发出不同的值

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,在观察state.distinct()时,只有当它真正发生变化时,您才会获得更新的值。

有时另一个扩展也很有用:

fun <T> LiveData<T>.squashUpdates() = MediatorLiveData<T>().apply {
    addSource(this@squashUpdates) {
        postValue(it)
    }
}
Run Code Online (Sandbox Code Playgroud)

通常,当LiveDatavalue 更改时,它会立即通知同一堆栈帧中的所有观察者。假设您有MediatorLiveData多个来源的a和更改值,例如:

isHovered.value = true
isSelected.value = true
Run Code Online (Sandbox Code Playgroud)

state值将连续更改两次State(true, false)并且State(true, true). 即使distinct从上面使用,它们也会被调度,因为值实际上是不同的。squashUpdates在这种情况下,通过将调度延迟到堆栈帧的末尾,仅调度最后一个值来提供帮助。