如何在视图模型的 init 块中包含合并的流

How to include a merged flow in init block of viewmodel

提问人:Alele 提问时间:11/1/2023 更新时间:11/2/2023 访问量:58

问:

我在 init 块中编写了代码,该代码合并了 4 个流 () 并输出一个修改 值的单个流。问题在于,当 4 个流中的任何一个发生变化时,的值不会改变。为什么_totalAmount的价值没有改变?运行代码时没有错误。merge(periodStart, periodEnd, selectedPeriodStart, selectedPeriodEnd)_totalAmount_totalAmount

_totalAmount的值不会改变

val _totalAmount = MutableStateFlow(0)
val totalAmount = _totalAmount.asStateFlow()

init{    
        merge(periodStart, periodEnd, selectedPeriodStart, selectedPeriodEnd)
            .map{                
                withContext(Dispatchers.IO){
                    _totalAmount.value = soldItemRepo.getTotalAmountSoldPerPeriod(
                        periodStart.value ?: selectedPeriodStart.value,
                        periodEnd.value ?: selectedPeriodEnd.value
                    ).first()
                }
            }
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
                initialValue = 0
            )
    }

我知道,如果我在视图模型的主体中包含合并的流,如下所示,那么每当 4 个流中的任何一个发生变化时,_totalAmount的值都会发生变化。

totalAmount 的值会发生变化

val totalAmount = merge(periodStart, periodEnd, selectedPeriodStart, selectedPeriodEnd)
        .map{
            withContext(Dispatchers.IO){
                soldItemRepo.getTotalAmountSoldPerPeriod(
                    periodStart.value ?: selectedPeriodStart.value,
                    periodEnd.value ?: selectedPeriodEnd.value
                ).first()
            }
        }.stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
            initialValue = 0)

提出问题的原因

我有几个变量取决于 ,这 4 个流程。我不想像上面那样继续声明变量。我想要像下面的代码一样的东西periodStart, periodEnd, selectedPeriodStart, selectedPeriodEndtotalAmount

这是我想实现的一个例子。

merge(periodStart, periodEnd, selectedPeriodStart, selectedPeriodEnd).map{
val a = {...}
val b = {...}
val c = {...}
...
}

如果您需要任何澄清,请询问。

kotlin kotlin-flow

评论

1赞 Mohsen 11/1/2023
尝试收集流,而不是映射

答:

0赞 cesonha 11/2/2023 #1

我认为对于这个用例,一个更好的运算符是组合所有流的值并根据需要使用它们(更改多个变量或其他流的值)。您可以在此处阅读有关它及其差异的更多信息:combinecombinemerge

我已经测试过了:

    val unitsFlow = MutableStateFlow(0)
    val tensFlow = MutableStateFlow(0)
    val hundredsFlow = MutableStateFlow(0)

    private var a = 0
    private var b = 0
    private var c = 0
    private val exampleFlow = MutableStateFlow(0)

    init {
        viewModelScope.launch {
            combine(unitsFlow, tensFlow, hundredsFlow) { units, tens, hundreds ->
                Triple(units, tens, hundreds) // just concatenate the most recent values (no transformation)
            }.collectLatest { (units, tens, hundreds) ->
                // update several variables (or other flows) using the most recent values
                a = units * 1
                b = (units * 1) + (tens * 10)
                c = (units * 1) + (tens * 10) + (hundreds * 100)
                exampleFlow.update { a + b + (hundreds * 2) }
            }
        }
    }

我使用了一些随机变量和流程来说明您想要的内容。由于您只想在不转换其值的情况下组合多个流,因此该部分只是简单地将它们放在一起,我不确定您是否可以跳过这个样板部分,但有些人已经解决了这个问题combineTriple

我用过,但你也可以用,这取决于你的用例。collectLatestcollect

尽管我已经对此进行了测试并且它正在工作,但我不知道直接从 ViewModel 的块中收集此组合流是否是一种好做法,也许将其放在变量中只是为了确保您可以在需要时轻松取消它。initJob

我会更深入地研究这部分,如果这真的是值得担心的事情,也许有人可以补充。