从多个状态流收集

Azi*_*mov 21 android kotlin kotlin-coroutines kotlin-stateflow

我的 viewModel 中有 2 个 stateFlow。为了将它们收集到片段中,我必须启动协程两次,如下所示:

    lifecycleScope.launchWhenStarted {
        stocksVM.quotes.collect {
            if (it is Resource.Success) {
                it.data?.let { list ->
                    quoteAdapter.submitData(list)
                }
            }
        }
    }

    lifecycleScope.launchWhenStarted {
        stocksVM.stockUpdate.collect {
            log(it.data?.data.toString())
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果我有更多的 stateFlow,我必须分别启动协程。有没有更好的方法来处理我的片段/活动或其他地方的多个 stateFlow?

Nag*_*obi 49

您将需要不同的协程,因为它collect()是一个挂起函数,会挂起直到您Flow终止。

对于收集多个流,当前推荐的方法是:

lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        launch {
          stocksVM.quotes.collect { ... }   
        }
    
        launch {
            stocksVM.stockUpdate.collect { ... }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,问题在于launchWhenStarted,虽然您新发出的项目不会被处理,但您的生产者仍将在后台运行。

我肯定会读一下这篇文章,因为它很好地解释了当前的最佳实践:https://medium.com/androiddevelopers/a-safer-way-to-collect-flows-from-android-uis-23080b1f8bda


Raw*_*sto 11

如果有人想知道如何在同一个块中发出多个流viewModelScope.launch,这与 Robert 的答案相同。即如下

viewModelScope.launch {
    launch {
        exampleFlow1.emit(data)
    }
    launch {
        exampleFlow2.emit(data)
    }
}
Run Code Online (Sandbox Code Playgroud)


Fut*_*one 9

您可以选择混合多个流。

在 中使用函数mergeor 。当然,这两个函数的用法是不同的。combinekotlin


添加:

如果Flow没有处理,则开启多个协程进行collect():

fun main() {
    collectFlow()
}

fun emitStringElem(): Flow<String> = flow {
    repeat(5) {
        delay(10)
        emit("elem_$it")
    }
}

fun emitIntElem(): Flow<Int> = flow {
    repeat(10) {
        delay(10)
        emit(it)
    }
}
Run Code Online (Sandbox Code Playgroud)

打开两个协程集合结果是:

From int Flow: item is: 0
From string Flow: item is: elem_0
From int Flow: item is: 1
From string Flow: item is: elem_1
From int Flow: item is: 2
From string Flow: item is: elem_2
From int Flow: item is: 3
From string Flow: item is: elem_3
From int Flow: item is: 4
From string Flow: item is: elem_4
From int Flow: item is: 5
From int Flow: item is: 6
From int Flow: item is: 7
From int Flow: item is: 8
From int Flow: item is: 9
Run Code Online (Sandbox Code Playgroud)

合并两个流

From int Flow: item is: 0
From string Flow: item is: elem_0
From int Flow: item is: 1
From string Flow: item is: elem_1
From int Flow: item is: 2
From string Flow: item is: elem_2
From int Flow: item is: 3
From string Flow: item is: elem_3
From int Flow: item is: 4
From string Flow: item is: elem_4
From int Flow: item is: 5
From int Flow: item is: 6
From int Flow: item is: 7
From int Flow: item is: 8
From int Flow: item is: 9
Run Code Online (Sandbox Code Playgroud)

结果是:

0
elem_0
1
elem_1
2
elem_2
3
elem_3
4
elem_4
5
6
7
8
9
Run Code Online (Sandbox Code Playgroud)

合并两个流:

fun margeFlow() = runBlocking {
    merge(
        emitIntElem().map {
            it.toString()
        }, emitStringElem()
    ).collect {
        println(it)
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

0 combine elem_0
1 combine elem_0
1 combine elem_1
2 combine elem_2
3 combine elem_3
4 combine elem_4
5 combine elem_4
6 combine elem_4
7 combine elem_4
8 combine elem_4
9 combine elem_4
Run Code Online (Sandbox Code Playgroud)


Ten*_*r04 7

就像 @R\xc3\xb3bertNagy 所说,你不应该使用launchWhenStarted. 但是有一种替代语法可以以正确的方式执行此操作,而无需执行嵌套launches:

\n
stocksVM.quotes\n    .flowOnLifecycle(Lifecycle.State.STARTED)\n    .onEach { \n        if (it is Resource.Success) {\n            it.data?.let { list ->\n                quoteAdapter.submitData(list)\n            }\n        }\n    }.launchIn(lifecycleScope)\n\nstocksVM.stockUpdate\n    .flowOnLifecycle(Lifecycle.State.STARTED)\n    .onEach { \n        log(it.data?.data.toString())\n    }.launchIn(lifecycleScope)\n
Run Code Online (Sandbox Code Playgroud)\n