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
}
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
}