Kotlin流:奇怪的行为,代码执行了3次,而应该只执行一次

问题描述

我最近开始研究一个使用kotlin flow + coroutine的项目,发现了一个奇怪的东西,我编写了一些代码来重现问题,这是代码

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.channels.*

sealed class ViewIntent {
    class Initial : ViewIntent()
    class Refresh : ViewIntent()
    class Click: ViewIntent()
}

class ViewState

@kotlinx.coroutines.ExperimentalCoroutinesApi
fun Flow<ViewIntent>.toEvent() : Flow<Int> {
    return merge(
       filterisinstance<ViewIntent.Initial>().map{1},filterisinstance<ViewIntent.Refresh>().map{2},filterisinstance<ViewIntent.Click>().map{3},)
}

@kotlinx.coroutines.ExperimentalCoroutinesApi
fun main() = runBlocking {
    val intentFlow = listof(ViewIntent.Initial()).asFlow()
    intentFlow.onEach { println(it) }
    .toEvent()
    .collect()

}

代码输出如下:

ViewIntent$Initial@2ff5659e
ViewIntent$Initial@2ff5659e
ViewIntent$Initial@2ff5659e

让我感到困惑的是为什么为什么ViewIntent $ Initial @ 2ff5659e被显示3次?如果删除.toEvent(),则仅显示一个ViewIntent $ Initial @ 2ff5659e。

解决方法

因为Flow是冷流。每次您呼叫filterIsInstance时,上游都会发出ViewIntent.Initial()

将来,SharedFlow将被添加到库中,请参见pull request,我们可以这样写:

@kotlinx.coroutines.ExperimentalCoroutinesApi
fun Flow<ViewIntent>.toEvent(coroutineScope: CoroutineScope) : Flow<Int> {
    val shared = this.shareIn(
            coroutineScope,replay = 0,started = SharingStarted.WhileSubscribed()
    )

    return merge(
       shared.filterIsInstance<ViewIntent.Initial>().map{1},shared.filterIsInstance<ViewIntent.Refresh>().map{2},shared.filterIsInstance<ViewIntent.Click>().map{3},)
}