MutableStateFlow 强制更新/通知收集器

MutableStateFlow force update / notify collector

如果更新值等于旧值 (source). I've found a

MutableStateFlow 不会通知收集器,但它不能很好地扩展复杂值。

解决方法:使用 copy() 复制数据 classes 并使用 toList()/toMutableList().

列出数据

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

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:具有多个依赖项的复杂数据 class 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.valueNotDistinct MutableStateFlow.value
    -> 问题:MutableStateFlow.value 必须可以为空
var <T> MutableStateFlow<T?>.valueNotDistinct: T?
    get() = null
    set(newValue) {
        value = null
        value = newValue
    }
  • 使用 MutableSharedFlow,不检查相等性
    -> 问题:性能不佳,没有 value 属性

我想要的是简单地在每次发出时通知收集器,但我不知道该怎么做,因为 MutableStateFlow 似乎没有“强制通知”功能。

StateFlow 文档指出:

Strong equality-based conflation

Values in state flow are conflated using Any.equals comparison in a similar way to distinctUntilChanged operator. It is used to conflate incoming updates to value in MutableStateFlow and to suppress emission of the values to collectors when new value is equal to the previously emitted one. State flow behavior with classes that violate the contract for Any.equals is unspecified.

解决方法是将 equals 方法覆盖为始终 return false。所以数据 class 对你的情况没有帮助。

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
}