在不发送给收集器的情况下更新 MutableStateFlow

Updating MutableStateFlow without emitting to collectors

在一个 Android 项目中,我们目前正在尝试在我们的视图模型中从 LiveData 切换到 StateFlow。但是对于一些罕见的情况,我们需要更新我们的状态而不通知收集器有关更改。当我们想到流程的工作机制时,这听起来可能很奇怪,但我想知道它是否可行。任何真正的解决方案或解决方法将不胜感激。

我们可以公开另一个根据需要过滤项目的流,而不是直接公开状态流。

例如,我们可以在发出的项目中保留 shouldEmit 标志。或者使用任何其他过滤逻辑:

suspend fun main(): Unit = coroutineScope {
    launch {
        stateFlow.collect {
            println("Collected: $it")
        }
    }

    delay(100)
    setState(1)
    delay(100)
    setState(2)
    delay(100)
    setState(3, shouldEmit = false)
    delay(100)
    setState(4)
    delay(100)
    setState(5)
    delay(100)
}

private val _stateFlow = MutableStateFlow(EmittableValue(0))

val stateFlow = _stateFlow.filter { it.shouldEmit }
    .map { it.value }

fun setState(value: Int, shouldEmit: Boolean = true) {
    _stateFlow.value = EmittableValue(value, shouldEmit)
}

private data class EmittableValue<T>(
    val value: T,
    val shouldEmit: Boolean = true
)

我们还可以在对象中保留 shouldEmit 标志并将其切换 on/off 以暂时禁用发射。

如果您需要公开 StateFlow 而不仅仅是 Flow,这应该也是可能的,但您需要决定忽略的排放是否会影响其 value

如果您不需要在任何地方对真实状态做出反应,而只需要对公开发出的状态做出反应,我会将真实状态直接存储在 属性 中,而不是 MutableStateFlow。

private var trueState: MyState = MyState(someDefault)
private val _publicState = MutableStateFlow<MyState>()
val publicstate = _publicState.asStateFlow()

fun updateState(newState: MyState, shouldEmitPublicly: Boolean) {
    trueState = newState
    if (shouldEmitPublicly) {
        _publicState.value = newState
    }
}

如果您确实需要对其做出反应,包装器 class 和过滤(@broot 的解决方案)的一种替代方法是简单地保留两个独立的 StateFlow。