public fun <T> Flow<T>.buffer(capacity: Int = BUFFERED, onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND): Flow<T> {
...
}
缓冲区通过指定容量的通道发射,并在单独的协程中运行收集器。
通常,流是顺序的。这意味着所有运算符的代码都在同一个协程中执行。例如,考虑以下使用 onEach 和 collect 运算符的代码:
flowOf("A", "B", "C")
.onEach { println("1$it") }
.collect { println("2$it") }
它将由调用此代码的协程 Q 按以下顺序执行:
Q : -->-- [1A] -- [2A] -- [1B] -- [2B] -- [1C] -- [2C] -->--
因此,如果运算符的代码需要相当长的时间来执行,那么总执行时间将是所有运算符的执行时间之和。
buffer 运算符在执行期间为其应用的流创建一个单独的协程。考虑以下代码:
flowOf("A", "B", "C")
.onEach { println("1$it") }
.buffer() // <--------------- buffer between onEach and collect
.collect { println("2$it") }
它将使用两个协程来执行代码。调用此代码的协程 Q 将执行 collect,而 buffer 之前的代码将在单独的新协程 P 中与 Q 并发执行:
P : -->-- [1A] -- [1B] -- [1C] ---------->-- //
flowOf(...).onEach { ... }
|
| channel // buffer()
V
Q : -->---------- [2A] -- [2B] -- [2C] -->-- // collect
当运算符的代码需要一些时间来执行时,这会减少流的总执行时间。协程之间使用通道将协程 P 发出的元素发送到协程 Q。 如果 buffer 运算符之前的代码(在协程 P 中)比 buffer 运算符之后的代码(在协程 Q 中)快,则该通道将在某个时刻变满并暂停生产者协程 P 直到消费者协程 Q 赶上。capacity 参数定义此缓冲区的大小。
缓冲区溢出
默认情况下,当缓冲区溢出时,发射器将被挂起,以使收集器赶上。可以使用可选的 onBufferOverflow 参数覆盖此策略,以便永远不会挂起发射器。在这种情况下,在缓冲区溢出时,要么使用 DROP_OLDEST 策略删除缓冲区中最旧的值,并将最新发出的值添加到缓冲区中,要么使用 DROP_LATEST 策略删除正在发出的最新值,保持缓冲区不变。为了实现任一自定义策略,至少使用一个元素的缓冲区。
运算符融合
channelFlow、flowOn、buffer、produceIn 和 broadcastIn 的相邻应用总是融合在一起,以便只使用一个正确配置的通道来执行。
明确指定的缓冲区容量优先于 buffer() 或 buffer(Channel.BUFFERED) 调用,前者有效地请求任何大小的缓冲区。多个具有指定缓冲区大小的请求会生成一个缓冲区,其中包含所请求缓冲区大小的总和。
具有 onBufferOverflow 参数的非默认值的 buffer 调用会覆盖所有紧接在前面的 buffer 运算符,因为它永远不会挂起其上游,因此不会使用任何上游缓冲区。
概念实现
由于融合,buffer 的实际实现并不是微不足道的,但从概念上讲,它的基本实现等效于以下代码,可以使用 produce 协程构建器来生成通道并使用 consumerEach 扩展函数来使用它:
fun <T> Flow<T>.buffer(capacity: Int = DEFAULT): Flow<T> = flow {
coroutineScope { // limit the scope of concurrent producer coroutine
val channel = produce(capacity = capacity) {
collect { send(it) } // send all to channel
}
// emit all received values
channel.consumeEach { emit(it) }
}
}
Conflation 合流
capacity 参数为 Channel.CONFLATED 的此函数的使用是 buffer(onBufferOverflow = BufferOverflow.DROP_OLDEST) 的快捷方式,可通过单独的合并运算符获得。有关详细信息,请参阅其文档。
参数:
capacity - 协程之间缓冲区的类型/容量。允许的值与 Channel(…) 工厂函数中的相同:BUFFERED(默认情况下)、CONFLATED、RENDEZVOUS、UNLIMITED 或指示明确请求大小的非负值。
onBufferOverflow - 配置对缓冲区溢出的操作(可选,默认为 SUSPEND,仅当 capacity >= 0 或 capacity== Channel.BUFFERED 时支持,隐式创建至少具有一个缓冲元素的通道)。