【问题标题】:Combine two state flows into new state flow将两个状态流组合成新的状态流
【发布时间】:2020-12-24 23:32:57
【问题描述】:

我有两个状态流。是否可以将它们结合起来并获得新的状态流?从逻辑上讲,这应该是可能的,因为两个状态流都有初始值,但我看到 combine 函数只返回 Flow 而不是 StateFlow。

【问题讨论】:

  • 我想你找到了一些 medium.com/better-programming/…
  • 为什么你特别需要StateFlow
  • @LouisWasserman 在某些地方我只需要读取当前值而不收集它

标签: kotlin collections kotlin-flow


【解决方案1】:

到目前为止,我创建了函数:

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

【讨论】:

  • 你能展示你如何在代码中使用它吗?似乎对我不起作用
【解决方案2】:

您可以使用combine 运算符,然后将stateIn 函数用于由此产生的Flow

来自 kotlinx 协程存储库中的 stateIn documentation

stateIn 函数“将 cold Flow 转换为在给定协程范围内启动的 hot StateFlow,共享最近发出的值来自具有多个下游订阅者的上游流的单个运行实例。”

截至撰写本文时,它的签名是:

fun <T> Flow<T>.stateIn(
  scope: CoroutineScope,
  started: SharingStarted,
  initialValue: T
): StateFlow<T> (source)

因此,您应该能够对Flows 进行任何您需要的转换,包括将它们组合起来,然后最终使用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&lt;String&gt; 类型,并且您已成功将另外两个 StateFlows 合并为一个 StateFlow

【讨论】:

  • 和往常一样,在我发布此消息后大约 5 秒,我意识到您使用 stateIn 回答了您自己的问题。唉,我将把描述留给后代。
【解决方案3】:

与@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))

【讨论】:

    【解决方案4】:

    使用combine 运算符,它需要两个流和一个转换函数来组合两个流的结果。

     val int = MutableStateFlow(2)
     val double = MutableStateFlow(1.8)
     int.combine(double){ i, d ->
                i + d             
     }.collect(::println)
    

    【讨论】:

    • 我特别需要StateFlow,但是这个函数返回Flow
    • 现在 IDE 中的错误显示“没有足够的信息来推断类型变量 R”,它也要求进行转换。对此仍然很陌生,因此如果库已更新,请解释或修改您的答案。谢谢!
    【解决方案5】:

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

    Gist

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-10
      • 2016-09-11
      • 2019-07-16
      • 2020-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-30
      相关资源
      最近更新 更多