MutableStateFlow强制更新/通知收集器



MutableStateFlow不会通知收集器更新后的值是否等于旧值(源(。我已经找到了一个解决方法,但它不能很好地适应复杂的值。

解决方法:使用copy()复制数据类,使用toList()/toMutableList()复制列表。

示例1:使用变通方法重命名name的简单数据类WorkoutRoutine。这里没什么问题。

data class WorkoutRoutine(
var name: String,
)
val workoutRoutine = MutableStateFlow(WorkoutRoutine("Initial"))
                     
workoutRoutine.value.name = "Updated" // Doesn't notify collectors
                     
workoutRoutine.value = workoutRoutine.value.copy(name = "Updated") // Workaround: works

示例2:具有多个依赖项的复杂数据类WorkoutRoutine,使用变通方法将Set添加到WorkoutRoutine中的Exercise:这需要大量的copy()toMutableList()调用,这使得代码不可读。

data class WorkoutRoutine(
var name: String,
var exercises: MutableList<Exercise> = mutableListOf(Exercise())
)
   
data class Exercise(
var sets: MutableList<Set> = mutableListOf(Set())
)
   
data class Set(
var weight: Int? = null
)
   
val workoutRoutine = MutableStateFlow(WorkoutRoutine("Initial"))
// Doesn't notify collectors
workoutRoutine.value.apply {
exercises = exercises.also {
it[0].sets.add(Set())
}
}
// Workaround: works
workoutRoutine.value = workoutRoutine.value.copy(
exercises = workoutRoutine.value.exercises.toMutableList().also {
it[0] = it[0].copy(sets = it[0].sets.apply { add(Set()) })
}
)

我试过以下几种:

  • 添加强制更新MutableStateFlow.value的扩展值MutableStateFlow.valueNotDistinct
    ->问题:MutableStateFlow.value必须可以为null
var <T> MutableStateFlow<T?>.valueNotDistinct: T?
get() = null
set(newValue) {
value = null
value = newValue
}
  • 使用不检查相等性的MutableSharedFlow
    ->问题:没有性能,没有value属性

我想要的只是在每次发射时简单地通知收集器,但我不知道如何做到这一点,因为似乎没有"强制通知";函数

StateFlow文档指出:

基于强相等的合并

状态流中的值在类似于distinctUntilChanged运算符。它被用来混淆传入更新MutableStateFlow中的值并抑制发射当新值等于以前的发射了一个。具有违反Any.equals的合同未指明。

解决方法可以重写equals方法以始终返回false。因此,数据类对您的情况没有帮助。

class WorkoutRoutine() {
...
override fun equals(other: Any?): Boolean {
return false
}    
}

MutableStateFlow只是一个接口,所以如果你不喜欢默认实现的工作方式,你可以自己编写。这里有一个简单的实现,它使用MutableSharedFlow来支持它。它不进行比较,所以它总是会更新。

class NoCompareMutableStateFlow<T>(
value: T
) : MutableStateFlow<T> {
override var value: T = value
set(value) {
field = value
innerFlow.tryEmit(value)
}
private val innerFlow = MutableSharedFlow<T>(replay = 1)
override fun compareAndSet(expect: T, update: T): Boolean {
value = update
return true
}
override suspend fun emit(value: T) {
this.value = value
}
override fun tryEmit(value: T): Boolean {
this.value = value
return true
}
override val subscriptionCount: StateFlow<Int> = innerFlow.subscriptionCount
@ExperimentalCoroutinesApi override fun resetReplayCache() = innerFlow.resetReplayCache()
override suspend fun collect(collector: FlowCollector<T>): Nothing = innerFlow.collect(collector)
override val replayCache: List<T> = innerFlow.replayCache
}

将MutableStateFlowsupdate函数与数据类copy函数结合使用,可以安全地更新StateFlow:

data class WorkoutRoutine(
var name: String,
var exercises: List<Exercise> = emptyList()
)

data class Exercise(
var sets: List<Set> = emptyList()
)

data class Set(
val weight: Int? = null
)

val workoutRoutine = MutableStateFlow(WorkoutRoutine("Initial"))
// Doesn't notify collectors
workoutRoutine.update { currentState ->
currentState.copy(
exercises = (currentState.exercises.firstOrNull()?.sets ?: emptyList()) + Set()
}
}

最新更新