Kotlin Flow 多个操作符的理解

190 阅读3分钟

拿一个去重的操作符*distinctUntilChanged*来分析

val numbers = flowOf(1, 1, 2, 2, 3, 4, 4, 4, 5)
numbers.distinctUntilChanged.collect {
 println(it)
} 

构造单个Flow

先生成一个Flow,这个Flow主要是循环发送数据

public fun <T> flowOf(vararg elements: T): Flow<T> = flow {
for (element in elements) {
        emit(element)
    }
} 

Flow是冷流,意思就是只有你调用collect方法它才触发发送emit数据的方法,为啥调用collect方法会触发数据?

看一下最终构造Flow对象的方法

internal inline fun <T> unsafeFlow(@BuilderInference crossinline block: suspend FlowCollector<T>.() -> Unit): Flow<T> {
    return object : Flow<T> {
        override suspend fun collect(collector: FlowCollector<T>) {
            collector.block()
        }
    }
}

Flow的关键方法是collect,在调用这个方法的时候,才去执行,最初的代码块collector.block()

也就是demo中这段代码

{
for (element in elements) {
        emit(element)
    }
} 

当我们调用collect方法之后,会触发上游发送方法emit,这里的demo上游是循环发送,每次发送一个数据。当上游调用发送方法之后,最后会在调用collect方法时候,传入的FlowCollector.emit实现类中接收到

collect {
 println(it)
} 

多个Flow链式操作

先来看一个去重复的Flow操作符distinctUntilChanged

val numbers = flowOf(1, 1, 2, 2, 3, 4, 4, 4, 5)
numbers.distinctUntilChanged().collect {
 println(it)
} 

先来简单看一下关键代码,这里我们不需要关注去重的逻辑如何实现的,只需要关注两个Flow之间,如何链式关联发送数据的,理解他们之前的如何关联底层关键逻辑,去重只是这个操作符具体业务的逻辑,这里我会去掉一些代码

public fun <T> Flow<T>.distinctUntilChanged(): Flow<T> =
distinctUntilChangedBy(
keySelector = defaultKeySelector, 
areEquivalent = defaultAreEquivalent
)
        
private fun <T> Flow<T>.distinctUntilChangedBy(
keySelector: (T) -> Any?,
areEquivalent: (old: Any?, new: Any?) -> Boolean
): Flow<T> = when {
    ...
    else -> DistinctFlowImpl(this, keySelector, areEquivalent)
}

private class DistinctFlowImpl<T>(
    private val upstream: Flow<T>,
    @JvmField val keySelector: (T) -> Any?,
    @JvmField val areEquivalent: (old: Any?, new: Any?) -> Boolean
): Flow<T> {
    override suspend fun collect(collector: FlowCollector<T>) {
        var previousKey: Any? = NULL
upstream.collect { value ->
val key = keySelector(value)
            @Suppress("UNCHECKED_CAST")
            if (previousKey === NULL || !areEquivalent(previousKey, key)) {
                previousKey = key
                collector.emit(value)
            }
        }
}
}

主要看一下DistinctFlowImplcollect方法的实现,DistinctFlowImpl内部持有了上一个Flow

DistinctFlowImpl Flow调用collect方法的时候,开始触发upstream.collect 上游Flowcollect方法,前面聊过,当触发一个Flowcollect方法的时候,才是发送数据的开始,所以

会先执行下面这段发送逻辑,也就是上游的代码块block

 {
for (element in elements) {
        emit(element)
    }
} 

上游发送数据之后,上游collect方法开始收到数据

upstream.collect { value ->
//上游开始收到数据
} 

然后由于下游持有上游的实例,这时候开始由下游发送数据

collector.emit(value)

下游发送数据之后,下游的collect方法也开始收到数据,这样数据就从上到下流动起来了。多个流之前链式操作。

numbers.distinctUntilChanged().collect {
 println(it)//下游开始收到数据
} 

总结

  • 多个流之前的操作,基本就是下游持有上游对象,在下游开始collect方法的时候,回去触发上游的collect,上游发送数据emit之后,上游收到collect数据,然后继续到下游发送emit数据,最后到下游收到collect数据。
  • 一般操作符都会生成一个新的Flow,新的Flow会持有上一个关联的Flow,一般我们称为上游。