=================================================================
学习 flow 一个绕不开的操作符就是 flowOn 了,以下面示例代码为例, flow 需要在协程中使用,下面的 emit(1)
会在 Dispatchers.Default 指定的线程中执行,而 println(it)
会在父协程所在线程中执行:
flow { emit(1) }.flowOn(Dispatchers.Default).collect { println(it) }
flow {}
的源码在上面已经看过了,就是以 block 代码块为参数创建了一个 SafeFlow 对象,接下来看一下 Flow.flowOn 的逻辑:
public fun Flow.flowOn(context: CoroutineContext): Flow {
checkFlowContext(context)
return when {
// 返回自身 Flow 实例
// 这里我们传入了 Dispatchers.Default, 所以不符合这个条件
context == EmptyCoroutineContext -> this
// SafeFlow 不是该类型,因此也不走这个流程,实际上 FusibleFlow 是当连续多次调用 flowOn 后会创建的 Flow 对象
this is FusibleFlow -> fuse(context = context)
// 逻辑走到这里
else -> ChannelFlowOperatorImpl(this, context = context)
}
}
在上面已经对流程注释了一下,因此上述实例代码转换一下即为: SafeFlow.flowOn.collect {} --> ChannelFlowOperatorImpl.collect {}
, 这里注意一下创建 ChannelFlowOperatorImpl 对象时传入的两个参数,第一个 this 指的是之前的 SafeFlow 对象,第二个 context 参数即是我们传入的调度器,它是一个协程上下文。
ChannelFlowOperatorImpl.collect 实现在父类 ChannelFlowOperator.collect 中,该方法如果发现传入的 coroutineContext 上下文中没有携带调度器,即我们调用 flowOn 时没有传入 Dispatchers 等调度器,则会直接调用上一层 SafeFlow 的 collect 方法(代码不贴了),否则接着调用父类 ChannelFlow 中的 collect 方法,我们直接看 flowOn 中传入了调度器后的逻辑:
internal abstract class ChannelFlowOperator<S, T>(
@JvmField protected val flow: Flow,
context: CoroutineContext,
capacity: Int,
onBufferOverflow: BufferOverflow
) : ChannelFlow(context, capacity, onBufferOverflow) {
override suspend fun collect(collector: FlowCollector) {
// 判断 coroutineContext 逻辑
// …
super.collect(collector) // 调用父类 ChannelFlow 中方法
}
}
public abstract class ChannelFlow(
// upstream context
@JvmField public val context: CoroutineContext,
// buffer capacity between upstream and downstream context
@JvmField public val capacity: Int,
// buffer overflow strategy
@JvmField public val onBufferOverflow: BufferOverflow
) : FusibleFlow {
override suspend fun collect(collector: FlowCollector): Unit =
coroutineScope {
collector.emitAll(produceImpl(this))
}
public open fun produceImpl(scope: CoroutineScope): ReceiveChannel =
scope.produce(context, produceCapacity, onBufferOverflow, start = CoroutineStart.ATOMIC, block = collectToFun)
}
这里可以看到 ChannelFlowOperatorImpl.collect 最后会走到 collector.emitAll(produceImpl(this))
生产消费的逻辑,我们分步骤看一下生产和接收的流程。
首先看上面 produceImpl 方法:
internal fun CoroutineScope.produce(
context: CoroutineContext = EmptyCoroutineContext,
capacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
start: CoroutineStart = CoroutineStart.DEFAULT,
onCompletion: CompletionHandler? = null,
@BuilderInference block: suspend ProducerScope.() -> Unit
): ReceiveChannel {
val channel = Channel(capacity, onBufferOverflow)
val newContext = newCoroutineContext(context)
val coroutine = ProducerCoroutine(newContext, channel)
if (onCompletion != null) coroutine.invokeOnCompletion(handler = onCompletion)
coroutine.start(start, coroutine, block)
return coroutine
}
看到这个方法,是不是很熟悉呢?参考之前的 Kotlin之深入理解协程工作原理 的文章可以知道,这里的 produce 方法其实就是启动了一个新的协程,该协程执行的代码块 block 是传入的 collectToFun 参数,接着找 collectToFun 可以发现它会取 ChannelFlowOperator.collectTo 方法:
// ChannelFlowOperator
protected override suspend fun collectTo(scope: ProducerScope) =
// flowCollect 方法实现在子类 ChannelFlowOperatorImpl 中
flowCollect(SendingCollector(scope))
// ChannelFlowOperatorImpl
internal class ChannelFlowOperatorImpl(
flow: Flow,
context: CoroutineContext = EmptyCoroutineContext,
capacity: Int = Channel.OPTIONAL_CHANNEL,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
) : ChannelFlowOperator<T, T>(flow, context, capacity, onBufferOverflow) {
override suspend fun flowCollect(collector: FlowCollector) =
// 这个 flow 就是上层传入的 SafeFlow 对象
flow.collect(collector)
}
根据之前的解析, flow.collect(collector)
中的 flow 是 SafeFlow 对象,其 collect 方法会执行 SafeFlow 中传入的代码块(即flow {}
),这个代码块中调用了 collector.emit(1)
方法(上面代码可以看出此时的 collector 是 SendingCollector 实例),因此我们看看 SendingCollector.emit 方法做了什么:
public class SendingCollector(
private val channel: SendChannel
) : FlowCollector {
override suspend fun emit(value: T): Unit = channel.send(value)
}
于是可以知道 produceImpl 方法就是启动了一个新的协程,然后在协程中执行上层 flow 对象(所以 flowOn 会对它上游的部分起作用)中的代码块(里面调用了 SendingCollector.emit 方法),然后通过 Channel.send 方法把这个 value 发送出去。
上面看了启动协程并在其内通过 Channel 发送数据的流程,这里看一下数据是怎么接收的,回到最开始的代码,从 collector.emitAll(channel)
开始,这个 channel 参数就是上一节上调用 send 发送数据的那个 channel 对象:
public suspend fun FlowCollector.emitAll(channel: ReceiveChannel): Unit =
emitAllImpl(channel, consume = true)
private suspend fun FlowCollector.emitAllImpl(channel: ReceiveChannel, consume: Boolean) {
ensureActive()
var cause: Throwable? = null
try {
while (true) {
val result = run { channel.receiveCatching() }
if (result.isClosed) {
result.exceptionOrNull()?.let { throw it }
break // returns normally when result.closeCause == null
}
emit(result.getOrThrow())
}
} catch (e: Throwable) {
cause = e
throw e
} finally {
if (consume) channel.cancelConsumed(cause)
}
}
这里可以看到开了一个无限循环,然后通过 Channel 去接收数据,并通过 emit 方法把接收的值发射出去,至于调用这个 emit 方法的 FlowCollector 对象是谁呢?再回到一开始 flow { emit(1) }.flowOn(Dispatchers.Default).collect { println(it) }
示例中最后面调用的 collect 方法,结合上一章 collect 的解析,可以知道这个 FlowCollector 就是通过 collect 方法传入的代码块创建的对象:
public suspend inline fun Flow.collect(crossinline action: suspend (value: T) -> Unit): Unit =
collect(object : FlowCollector {
override suspend fun emit(value: T) = action(value)
})
于是最终在收到数据并 emit 后,会把 value 传递给 collect {}
中的代码块去执行。
以下面代码为例:
flow { emit(1) }.flowOn(Dispatchers.IO).flowOn(Dispatchers.Main).collect { println(it) }
由之前的解析可以知道首先会调用到这里:
public fun Flow.flowOn(context: CoroutineContext): Flow {
checkFlowContext(context)
return when {
context == EmptyCoroutineContext -> this
this is FusibleFlow -> fuse(context = context)
else -> ChannelFlowOperatorImpl(this, context = context)
}
}
第一次调用 flowOn 时返回的是 ChannelFlowOperatorImpl 对象,查看其继承关系可以知道它实现了 FusibleFlow 接口,因此第二次调用 flowOn 时会走 fuse(context = context)
逻辑:
public abstract class ChannelFlow(
// upstream context
@JvmField public val context: CoroutineContext,
// buffer capacity between upstream and downstream context
@JvmField public val capacity: Int,
// buffer overflow strategy
@JvmField public val onBufferOverflow: BufferOverflow
) : FusibleFlow {
public override fun fuse(context: CoroutineContext, capacity: Int, onBufferOverflow: BufferOverflow): Flow {
val newContext = context + this.context
val newCapacity: Int
// 处理 newCapacity 等计算逻辑
// …
if (newContext == this.context && newCapacity == this.capacity && newOverflow == this.onBufferOverflow)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
要如何成为Android架构师?
搭建自己的知识框架,全面提升自己的技术体系,并且往底层源码方向深入钻研。
大多数技术人喜欢用思维脑图来构建自己的知识体系,一目了然。这里给大家分享一份大厂主流的Android架构师技术体系,可以用来搭建自己的知识框架,或者查漏补缺;
对应这份技术大纲,我也整理了一套Android高级架构师完整系列的视频教程,主要针对3-5年Android开发经验以上,需要往高级架构师层次学习提升的同学,希望能帮你突破瓶颈,跳槽进大厂;
最后我必须强调几点:
1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。
2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要! 最后你达到什么水平取决你消化了多少知识
3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。
你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
927)]
对应这份技术大纲,我也整理了一套Android高级架构师完整系列的视频教程,主要针对3-5年Android开发经验以上,需要往高级架构师层次学习提升的同学,希望能帮你突破瓶颈,跳槽进大厂;
最后我必须强调几点:
1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。
2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要! 最后你达到什么水平取决你消化了多少知识
3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。
你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境