本文分析示例代码如下:

flow {
    emit(1)
}.flowOn(Dispatchers.Main).onEach {
    Log.d("liduo", "onCreate1: $it")
}.collect {
    Log.d("liudo", "onCreate2: $it")
}

一.链式调用原理

协程Flow中,除了flow办法外的其他办法,都是Flow接口的扩展办法,这些办法也被称作操作符。而flow办法用于创立一个类型为Flow的目标,一般用于创立整条链中的第一个Flow目标。

在协程Flow中,除了collect办法外的其他办法,在被调用后都会回来一个Flow目标。这儿的collect办法不是在Flow接口中界说的办法,而是Flow接口的扩展办法,一个操作符,一般用于在整条链创立结束后,最为最终一个操作符调用,触发整条链的执行。

这样,经过扩展办法与回来目标的一致,实现了链式调用。从第一个操作符的调用,到最终一个操作符的调用,形成了一个类似于上下流的流式结构。

因为后一个操作符是前一个操作符回来的Flow目标的扩展办法,因此后一个操作符会持有前一个Flow目标的引用,因此可以经过调用上一个Flow目标的collect办法,触发上游Flow目标的执行。

Kotlin协程:Flow的流式调用原理
一起,想要调用Flow目标的collect办法,需要向collect办法内传递一个类型为FlowCollector的参数。这样,上游的Flow目标就会持有一个下流的类型为FlowCollector目标的引用。而上游Flow目标的参数block,正是下流FlowCollector目标的扩展办法。

经过每一级Flow目标的层层触发,最终会调用到最上游Flow目标的collect办法。这时,最上游Flow目标的block参数会执行。当需要向下流Flow目标发送值时,会调用emit办法,因为参数block是下流FlowCollector目标的扩展办法,因此调用的emit办法也是下流FlowCollector目标实现的emit办法。

这样上游发送的值层层向下传递,在中间进程或许会被改换处理或直接透传,最终传递到最下流的操作符并调用其参数block处理。这儿的block因为是最下流操作符的block,因此它不是FlowCollector目标的扩展办法,因此也就不能调用emit办法发送值,只能进行处理操作。