9

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

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

如果我有更多的 stateFlow,我必须分别启动协程。有没有更好的方法来处理我的 Fragment/Activity 或任何地方的多个 stateFlow?

4

4 回答 4

19

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

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

lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        launch {
          stocksVM.quotes.collect { ... }   
        }
    
        launch {
            stocksVM.stockUpdate.collect { ... }
        }
    }
}

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

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

于 2021-06-02T07:55:20.133 回答
5

您可以选择混合多个流。

使用函数mergecombinekotlin. 当然,这两个函数的用法是不一样的。


添加:

如果 Flow 没有被处理,打开多个 Coroutine 去 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)
    }
}

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

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

合并两个流

fun margeFlow() = runBlocking {
    merge(
        emitIntElem().map {
            it.toString()
        }, emitStringElem()
    ).collect {
        println(it)
    }
}

结果是:

0
elem_0
1
elem_1
2
elem_2
3
elem_3
4
elem_4
5
6
7
8
9

合并两个流程:

fun combineFlow() = runBlocking {
    combine(emitIntElem(), emitStringElem()) { int: Int, str: String ->
        "$int combine $str"
    }.collect {
        println(it)
    }
}

结果是:

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
于 2021-06-02T06:25:49.487 回答
4

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

stocksVM.quotes
    .flowOnLifecycle(Lifecycle.State.STARTED)
    .onEach { 
        if (it is Resource.Success) {
            it.data?.let { list ->
                quoteAdapter.submitData(list)
            }
        }
    }.launchIn(lifecycleScope)

stocksVM.stockUpdate
    .flowOnLifecycle(Lifecycle.State.STARTED)
    .onEach { 
        log(it.data?.data.toString())
    }.launchIn(lifecycleScope)
于 2021-06-02T13:42:32.547 回答
1

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

viewModelScope.launch {
    launch {
        exampleFlow1.emit(data)
    }
    launch {
        exampleFlow2.emit(data)
    }
}
于 2021-09-08T15:22:34.710 回答