Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
Flow可以理解为一种用于数据流模型的Kotlin类型。形象化的比喻可以用管道的概念来描述,管道上方发射一些数据,管道下方来收集这些数据,不过有意思的一点是,管道的下方有一个类似于水龙头的东西,只有打开了,管道上方才会发射这些数据,这点在文章后面我们会仔细分析。
使用Flow
// 发射一个数据A
val stringFlow = flow {
Log.d(TAG, "before emit")
emit("A")
Log.d(TAG, "after emit")
}
lifecycleScope.launchWhenResumed {
// 收集发射过来的数据
stringFlow.collect { Log.d(TAG, it) }
}
最后的日志可以看出在Activity的onResume()之后收到了上方发射过来的数据
Flow的使用还是比较简单的:
- 通过
flow{}创建一个流对象,参数是一个FlowCollector.()->Unit的Lambada表达式,可以在此表达式内发射数据; - 然后通过
collect{}来接收和处理数据,这里的collect{}是一个挂起函数,必须在协程内才能调用,collect{}接收的参数也是一个action: suspend (value: T) -> Unit挂起的Lambada表达式。
flow{}做了什么
Flow是如何创建的
flow{}
|
|
|
public fun <T> flow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> = SafeFlow(block)
// Named anonymous object
private class SafeFlow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : AbstractFlow<T>() {
override suspend fun collectSafely(collector: FlowCollector<T>) {
collector.block()
}
}
flow{}其实就是创建了一个SafeFlow实例,SafeFlow是AbstractFlow的子类,而AbstractFlow是Flow的子类。
SafeFlow只有一个方法collectSafely(),里面只有我们创建flow{}传进来的Lambada表达式,到此为止我们可以看出flow{}只是创建了一个SafeFlow实例,其余都没做。
flow.collect{}做了什么
public suspend inline fun <T> Flow<T>.collect(crossinline action: suspend (value: T) -> Unit): Unit =
collect(object : FlowCollector<T> {
override suspend fun emit(value: T) = action(value)
})
flow.collect{}是Flow的扩展函数,内部只是调用了自己的collect()方法;从上面我们了解到这里的Flow其实就是SafeFlow对象,所以我们直接看SafeFlow.collect()方法即可。collect()内部传了一个FlowCollector对象,然后从其中的emit()方法拿到数据放入action()Lambada表达式中,这样我们就可以拿到数据随之处理了。
接着我们进入SafeFlow类中,并没发现有collect()方法的实现,于是进入它的父类AbstractFlow发现父类已经实现了collect()方法,下面看看此方法如果收集数据:
public abstract class AbstractFlow<T> : Flow<T>, CancellableFlow<T> {
@InternalCoroutinesApi
public final override suspend fun collect(collector: FlowCollector<T>) {
val safeCollector = SafeCollector(collector, coroutineContext)
try {
// 只是调用collectSafely()方法
collectSafely(safeCollector)
} finally {
safeCollector.releaseIntercepted()
}
}
}
AbstractFlow.collect()方法内,封装了一个SafeCollector对象,然后调用collectSafely()方法,看到这个方法是不是有点眼熟,没错它就是我们在上面解析flow{}中提到过了,创建SafeFlow对象中的唯一实现方法,到这我们就可以完全梳理下流程:
flow{}创建SafeFlow实例,内部实现了collectSafely()方法,方法内单纯调用了传入的{}表达式,在表达式内可以使用emit()发射数据;flow.collect{}就是调用了AbstractFlow.collect()方法,方法内部接着调用了collectSafely()方法;flow.collect{}调用之后,emit()发射的数据才会被接收并处理。
总结
由此可以看出,Flow是冷流模式,只有打开接收,才会去执行发射操作。