在看 flow 相关的源码,总是看到 FusibleFlow、ChannelFlow、ChannelFlowOperatorImpl 等内容,于是把 Channel 相关内容代码都过一遍
// flow 的操作符 public fun <T> Flow<T>.flowOn(context: CoroutineContext): Flow<T> { checkFlowContext(context) return when { context == EmptyCoroutineContext -> this this is FusibleFlow -> fuse(context = context) else -> ChannelFlowOperatorImpl(this, context = context) } }
Channel是什么
Kotlin
版的 BlockingQueue
, 我的个人理解是支持在协程环境下的使用的队列数据结构
根据 capacity
和 onBufferOverflow
获取不同 Channel
实例
public fun <E> Channel(
capacity: Int = RENDEZVOUS,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
onUndeliveredElement: ((E) -> Unit)? = null
): Channel<E> =
when (capacity) {
RENDEZVOUS -> {
if (onBufferOverflow == BufferOverflow.SUSPEND)
// step1.1: 溢出策略为挂起的时候,无须缓存区,挂起就好
RendezvousChannel(onUndeliveredElement)
else
// step1.2: 溢出丢弃策略,设置一个缓存区存放
ArrayChannel(1, onBufferOverflow, onUndeliveredElement)
}
// step2: 设置合并容量模式,因为会自动抛弃最老的数据, 其实我感觉这里只生效 DROP_OLDEST, 要么不判断,要么判断全
CONFLATED -> {
require(onBufferOverflow == BufferOverflow.SUSPEND) {
"CONFLATED capacity cannot be used with non-default onBufferOverflow"
}
ConflatedChannel(onUndeliveredElement)
}
// step3: 设置无限制模式,底层使用链表实现,因为容量无限制,相对应的溢出也不存在了
UNLIMITED -> LinkedListChannel(onUndeliveredElement)
// step4: 设置缓存模式,如果为挂起策略则容量为64(默认), 反之容量为1,因为剩余2种溢出策略不是丢弃最新的就是丢弃最老的,所以容量1足够
BUFFERED -> ArrayChannel(
if (onBufferOverflow == BufferOverflow.SUSPEND) CHANNEL_DEFAULT_CAPACITY else 1,
onBufferOverflow, onUndeliveredElement
)
// step5: 设置指定容量的的时候,创建 ArrayChannel
else -> {
if (capacity == 1 && onBufferOverflow == BufferOverflow.DROP_OLDEST)
step5.1: 与 CONFLATED相同直接复用
ConflatedChannel(onUndeliveredElement)
else
ArrayChannel(capacity, onBufferOverflow, onUndeliveredElement)
}
}
- Tips: 其实容量还有一个特殊值为
OPTIONAL_CHANNEL
, 是ChannelFlow
内部使用的,代表可选择是否使用Channel
,即能不用就不用;场景为如果非ContinuationInterceptor
上下文切换 并且 无buffer 的时候,不需要Channel
的介入
class ChannelFlowOperator{
// ...
override suspend fun collect(collector: FlowCollector<T>) {
// step1: 如果channel是可选的且没有buffer(flowOn/flowWith操作符)
if (capacity == Channel.OPTIONAL_CHANNEL) {
val collectContext = coroutineContext
val newContext = collectContext + context
// step2: 没有上下文发生改变,则开始原始流收集
if (newContext == collectContext)
return flowCollect(collector)
// step3: 不需要改变协程的线程环境
if (newContext[ContinuationInterceptor] == collectContext[ContinuationInterceptor])
return collectWithContextUndispatched(collector, newContext)
}
// step4: 创建 channel 开始收集
super.collect(collector)
}
// ...
}
让 Channel
转成 Flow
: 目的让 Channel
具备 Flow
的运算符
Channel
转换 flow
有2种实现方式,本质是同一种实现方式,只是是否可以被多次 collect
ReceiveChannel#consumeAsFlow()
: 只能被collect
一次,多次collect
时候会抛异常;ReceiveChannel#receiveAsFlow
: 能被collect
多次,但Channel
中的一条消息只能被一个collector
所消费, 俗称扇形消费
public fun <T> ReceiveChannel<T>.receiveAsFlow(): Flow<T> = ChannelAsFlow(this, consume = false)
public fun <T> ReceiveChannel<T>.consumeAsFlow(): Flow<T> = ChannelAsFlow(this, consume = true)
private class ChannelAsFlow<T>(
private val channel: ReceiveChannel<T>,
private val consume: Boolean,
context: CoroutineContext = EmptyCoroutineContext,
capacity: Int = Channel.OPTIONAL_CHANNEL,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
) : ChannelFlow<T>(context, capacity, onBufferOverflow) {
private val consumed = atomic(false)
private fun markConsumed() {
if (consume) {
check(!consumed.getAndSet(true)) { "ReceiveChannel.consumeAsFlow can be collected just once" }
}
}
// ...
override suspend fun collect(collector: FlowCollector<T>) {
// step1: 从 receiveAsFlow 或者 consumeAsFlow 构造可知,只会进入此分支
if (capacity == Channel.OPTIONAL_CHANNEL) {
// step2: 检查是否消费过
markConsumed()
// step3: 把 channel 的交给 collector 收集
collector.emitAllImpl(channel, consume)
} else {
super.collect(collector)
}
}
// ...
}
private suspend fun <T> FlowCollector<T>.emitAllImpl(channel: ReceiveChannel<T>, consume: Boolean) {
//step4: 检查协程是否正常
ensureActive()
var cause: Throwable? = null
try {
while (true) {
// step5: 获取 channel 中队头 => 由这里知道当前值被此 collector 消费,其他 collector 就获取不到了
val result = run { channel.receiveCatching() }
// step6: channel关闭的,则跳出循环
if (result.isClosed) {
result.exceptionOrNull()?.let { throw it }
break
}
// step7: 未关闭,弹出获取的值
emit(result.getOrThrow())
}
} catch (e: Throwable) {
cause = e
throw e
} finally {
// step8: 取消 channel
if (consume) channel.cancelConsumed(cause)
}
}
BroadcastChannel
: 广播Channel(已经被 StatedFlow
和 SharedFlow
替换)
-
当容量
capacity
设置为Conflated
, 实际会生成ConflatedBroadcastChannel
, 相当于溢出的时候每次丢弃最老的,并且内部只有一个状态存储值, 等价于StatedFlow
-
当容量
capacity
设置为大于0的时候,实际生成ArrayBroadcastChannel
, 完全被SharedFlow
功能代替,并且还多了replay
功能
ChannelFlow: 目的扩展Flow
功能,比如切换协程执行上下文、增加buffer等
FusibleFlow
: 目的是提供一个融合方法,可以返回一个进行自身上下文的切换、容量和溢出策略配置后的流
public interface FusibleFlow<T> : Flow<T> {
public fun fuse(
context: CoroutineContext = EmptyCoroutineContext,
capacity: Int = Channel.OPTIONAL_CHANNEL,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): Flow<T>
}
ChannelFlow
: 继承与FusibleFlow
ChannelFlow
其实算 Channel
场景的一个应用;Channel
自身提供了 capacity
和 onBufferOverflow
配置,可以配置当作背压来使用;同时 Channel
本身也是一个生产消费者模型的数据结构,可以在其他协程生产,在 collector
协程进行消费;所以 Kotlin
官方使用 Channel 来实现 flowOn
、buffer
流操作符的实现
则开头所贴的代码就好理解了
public fun <T> Flow<T>.flowOn(context: CoroutineContext): Flow<T> {
checkFlowContext(context)
return when {
// step1: 如果没有上下文切换,则返回自身
context == EmptyCoroutineContext -> this
// step2: 如果自身支持切换的话,则调用自身方法进行切换
this is FusibleFlow -> fuse(context = context)
// step3: ChannelFlowOperatorImpl 继承 ChannelFlow,ChannelFlow 继承 FusibleFlow,走到此分支,则代表是一个普通流,使用 ChanelFlowOperatorImpl 进行包装切换上下文
else -> ChannelFlowOperatorImpl(this, context = context)
}
}
ChannelFlowOperatorImpl
源码解析: 从 ChannelFlowOperatorImpl#collect
为起点
internal class ChannelFlowOperatorImpl<T>(
flow: Flow<T>,
context: CoroutineContext = EmptyCoroutineContext,
capacity: Int = Channel.OPTIONAL_CHANNEL,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
) : ChannelFlowOperator<T, T>(flow, context, capacity, onBufferOverflow) {
internal val collectToFun: suspend (ProducerScope<T>) -> Unit
get() = { collectTo(it) }
// Tips: 从上述 flowOn 可知, 不会对流进行无限包装,则返回原始流即可
override fun dropChannelOperators(): Flow<T> = flow
// step1: 开始收集内容
override suspend fun collect(collector: FlowCollector<T>): Unit =
coroutineScope {
val channel: ReceiveChannel<T> = produceImpl(this)
// step10: 传入的 collector 接受 channel 传过来的内容
collector.emitAll(channel)
}
public open fun produceImpl(scope: CoroutineScope): ReceiveChannel<T> =
// step2: 启动一个新的协程去执行 collectToFun,即 collectTo
scope.produce(context, produceCapacity, onBufferOverflow, start = CoroutineStart.ATOMIC, block = collectToFun)
protected override suspend fun collectTo(scope: ProducerScope<T>) =
// step8: 创建 collector 进行初始流收集
flowCollect(SendingCollector(scope))
override suspend fun flowCollect(collector: FlowCollector<T>) =
flow.collect(collector)
}
public class SendingCollector<T>(
private val channel: SendChannel<T>
) : FlowCollector<T> {
// step9: 把初始流的内容发射到 channel 中
override suspend fun emit(value: T): Unit = channel.send(value)
}
internal fun <E> CoroutineScope.produce(
context: CoroutineContext = EmptyCoroutineContext,
capacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
start: CoroutineStart = CoroutineStart.DEFAULT,
onCompletion: CompletionHandler? = null,
@BuilderInference block: suspend ProducerScope<E>.() -> Unit
): ReceiveChannel<E> {
// step3: 按照容量和溢出策略配置channel
val channel = Channel<E>(capacity, onBufferOverflow)
// step4: 获得新的协程环境
val newContext = newCoroutineContext(context)
// step5: channel 和 协程上下文
val coroutine = ProducerCoroutine(newContext, channel)
// step6: 注册协程完成回调
if (onCompletion != null) coroutine.invokeOnCompletion(handler = onCompletion)
// step7: 启动协程,会执行 ChannelFlowOperator#collectToFun => ChannelFlowOperator#collectTo
coroutine.start(start, coroutine, block)
return coroutine
}