将两个状态流合并为新的状态流
Combine two state flows into new state flow
我有两个状态流。是否可以将它们组合起来并获得新的状态流?从逻辑上讲,这应该是可能的,因为两个状态流都有初始值,但正如我所见,组合函数 returns 只是 Flow 而不是 StateFlow。
使用combine
运算符,需要两个流和一个转换函数来合并两个流的结果。
val int = MutableStateFlow(2)
val double = MutableStateFlow(1.8)
int.combine(double){ i, d ->
i + d
}.collect(::println)
到目前为止我创建了函数:
fun <T1, T2, R> combineState(
flow1: StateFlow<T1>,
flow2: StateFlow<T2>,
scope: CoroutineScope = GlobalScope,
sharingStarted: SharingStarted = SharingStarted.Eagerly,
transform: (T1, T2) -> R
): StateFlow<R> = combine(flow1, flow2) {
o1, o2 -> transform.invoke(o1, o2)
}.stateIn(scope, sharingStarted, transform.invoke(flow1.value, flow2.value))
您可以使用 combine
运算符,然后对由此产生的 Flow
使用 stateIn
函数。
来自 kotlinx 协程存储库中的 stateIn
documentation:
stateIn
函数“将 cold Flow
转换为 hot StateFlow
在给定的协程范围内启动,与多个下游订阅者共享上游流的单个 运行 实例最近发出的值。"
在撰写本文时,它的签名是:
fun <T> Flow<T>.stateIn(
scope: CoroutineScope,
started: SharingStarted,
initialValue: T
): StateFlow<T> (source)
因此,您应该能够对 Flow
进行任何需要的转换,包括组合它们,然后最终使用 stateIn
将它们转换回 StateFlow
。
它可能看起来像这样(也许创建一个拼字游戏游戏点数计算器):
val wordFlow = MutableStateFlow("Hi")
val pointFlow = MutableStateFlow(5)
val stateString = wordFlow.combine(pointFlow) { word, points ->
"$word is worth $points points"
}.stateIn(viewModelScope, SharingStarted.Eagerly, "Default is worth 0 points")
stateString
将是 StateFlow<String>
类型,您已成功将另外两个 StateFlows
合并为一个 StateFlow
.
合并 n 个状态流
@Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
inline fun <reified T, R> combineStateFlow(
vararg flows: StateFlow<T>,
scope: CoroutineScope = GlobalScope,
sharingStarted: SharingStarted = SharingStarted.Eagerly,
crossinline transform: (Array<T>) -> R
): StateFlow<R> = combine(flows = flows) {
transform.invoke(it)
}.stateIn(
scope = scope,
started = sharingStarted,
initialValue = transform.invoke(flows.map {
it.value
}.toTypedArray())
)
使用:
data class A(val a: String)
data class B(val b: Int)
private val test1 = MutableStateFlow(A("a"))
private val test2 = MutableStateFlow(B(2))
@Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
private val _isValidForm = combineStateFlow(
flows = arrayOf(test1, test2),
scope = viewModelScope
) { combinedFlows: Array<Any> ->
combinedFlows.map {
val doSomething = when (it) {
is A -> true
is B -> false
else -> false
}
}
}
与@Nikola Despotoski 类似的解决方案,但采用扩展函数的形式
/**
* Combines two [StateFlow]s into a single [StateFlow]
*/
fun <T1, T2, R> StateFlow<T1>.combineState(
flow2: StateFlow<T2>,
scope: CoroutineScope = GlobalScope,
sharingStarted: SharingStarted = SharingStarted.Eagerly,
transform: (T1, T2) -> R
): StateFlow<R> = combine(this, flow2) { o1, o2 -> transform.invoke(o1, o2) }
.stateIn(scope, sharingStarted, transform.invoke(this.value, flow2.value))
我有两个状态流。是否可以将它们组合起来并获得新的状态流?从逻辑上讲,这应该是可能的,因为两个状态流都有初始值,但正如我所见,组合函数 returns 只是 Flow 而不是 StateFlow。
使用combine
运算符,需要两个流和一个转换函数来合并两个流的结果。
val int = MutableStateFlow(2)
val double = MutableStateFlow(1.8)
int.combine(double){ i, d ->
i + d
}.collect(::println)
到目前为止我创建了函数:
fun <T1, T2, R> combineState(
flow1: StateFlow<T1>,
flow2: StateFlow<T2>,
scope: CoroutineScope = GlobalScope,
sharingStarted: SharingStarted = SharingStarted.Eagerly,
transform: (T1, T2) -> R
): StateFlow<R> = combine(flow1, flow2) {
o1, o2 -> transform.invoke(o1, o2)
}.stateIn(scope, sharingStarted, transform.invoke(flow1.value, flow2.value))
您可以使用 combine
运算符,然后对由此产生的 Flow
使用 stateIn
函数。
来自 kotlinx 协程存储库中的 stateIn
documentation:
stateIn
函数“将 cold Flow
转换为 hot StateFlow
在给定的协程范围内启动,与多个下游订阅者共享上游流的单个 运行 实例最近发出的值。"
在撰写本文时,它的签名是:
fun <T> Flow<T>.stateIn(
scope: CoroutineScope,
started: SharingStarted,
initialValue: T
): StateFlow<T> (source)
因此,您应该能够对 Flow
进行任何需要的转换,包括组合它们,然后最终使用 stateIn
将它们转换回 StateFlow
。
它可能看起来像这样(也许创建一个拼字游戏游戏点数计算器):
val wordFlow = MutableStateFlow("Hi")
val pointFlow = MutableStateFlow(5)
val stateString = wordFlow.combine(pointFlow) { word, points ->
"$word is worth $points points"
}.stateIn(viewModelScope, SharingStarted.Eagerly, "Default is worth 0 points")
stateString
将是 StateFlow<String>
类型,您已成功将另外两个 StateFlows
合并为一个 StateFlow
.
合并 n 个状态流
@Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
inline fun <reified T, R> combineStateFlow(
vararg flows: StateFlow<T>,
scope: CoroutineScope = GlobalScope,
sharingStarted: SharingStarted = SharingStarted.Eagerly,
crossinline transform: (Array<T>) -> R
): StateFlow<R> = combine(flows = flows) {
transform.invoke(it)
}.stateIn(
scope = scope,
started = sharingStarted,
initialValue = transform.invoke(flows.map {
it.value
}.toTypedArray())
)
使用:
data class A(val a: String)
data class B(val b: Int)
private val test1 = MutableStateFlow(A("a"))
private val test2 = MutableStateFlow(B(2))
@Suppress("CHANGING_ARGUMENTS_EXECUTION_ORDER_FOR_NAMED_VARARGS")
private val _isValidForm = combineStateFlow(
flows = arrayOf(test1, test2),
scope = viewModelScope
) { combinedFlows: Array<Any> ->
combinedFlows.map {
val doSomething = when (it) {
is A -> true
is B -> false
else -> false
}
}
}
与@Nikola Despotoski 类似的解决方案,但采用扩展函数的形式
/**
* Combines two [StateFlow]s into a single [StateFlow]
*/
fun <T1, T2, R> StateFlow<T1>.combineState(
flow2: StateFlow<T2>,
scope: CoroutineScope = GlobalScope,
sharingStarted: SharingStarted = SharingStarted.Eagerly,
transform: (T1, T2) -> R
): StateFlow<R> = combine(this, flow2) { o1, o2 -> transform.invoke(o1, o2) }
.stateIn(scope, sharingStarted, transform.invoke(this.value, flow2.value))