从多个状态流中收集
Collect from several stateflows
我的 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?
您可以选择混合多个流。
在kotlin
中使用函数merge
或combine
。当然这两个函数的用法是不一样的
添加:
如果不处理Flow,则开启多个Coroutines进行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
您将需要不同的协程,因为 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
正如@RóbertNagy 所说,你不应该使用 launchWhenStarted
。但是有一种替代语法可以以正确的方式进行操作,而不必执行嵌套的 launch
es:
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)
如果有人想知道如何在viewModelScope.launch
的同一个块中发出多个流,这与罗伯特的回答相同。即如下
viewModelScope.launch {
launch {
exampleFlow1.emit(data)
}
launch {
exampleFlow2.emit(data)
}
}
我的 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?
您可以选择混合多个流。
在kotlin
中使用函数merge
或combine
。当然这两个函数的用法是不一样的
添加:
如果不处理Flow,则开启多个Coroutines进行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
您将需要不同的协程,因为 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
正如@RóbertNagy 所说,你不应该使用 launchWhenStarted
。但是有一种替代语法可以以正确的方式进行操作,而不必执行嵌套的 launch
es:
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)
如果有人想知道如何在viewModelScope.launch
的同一个块中发出多个流,这与罗伯特的回答相同。即如下
viewModelScope.launch {
launch {
exampleFlow1.emit(data)
}
launch {
exampleFlow2.emit(data)
}
}