什么是buffer
- 在 上游(emit) 和 下游(collect) 之间插入一个有界/无界缓冲队列(底层是 Channel),把两端解耦:上游可以先"塞进队列就走",下游慢慢消费。
- 主要用于背压处理 与并发解耦。
scss
[ emitter ] --(buffer queue)--> [ collector ]
基本用法与参数
ini
flow.buffer(
capacity: Int = Channel.BUFFERED, // 常用:Channel.BUFFERED(≈64) / UNLIMITED / RENDEZVOUS(0)
onBufferOverflow: BufferOverflow = SUSPEND // 也可:DROP_OLDEST / DROP_LATEST
)
-
SUSPEND(默认) :队列满了就挂起上游,保证不丢数据。
-
DROP_OLDEST:丢最老的,保最新的一批("滑窗")。
-
DROP_LATEST:丢最新的(更少见)。
-
capacity = 0(RENDEZVOUS):相当于无缓冲,交接点对点同步,不解耦。
-
UNLIMITED:不建议轻易用,可能吃内存。
和其它操作符的区别
-
conflate() ≈ buffer(1, DROP_OLDEST):永远只保"最新值",中间值丢弃,适合"只关心最新状态"的 UI。
-
collectLatest / mapLatest :来了新值会取消前一个下游处理(不是丢缓存,而是取消处理过程),适合"处理很重但只要最新"的场景(比如渲染)。
-
buffer 不会取消处理,只影响排队与丢弃策略。
典型场景
-
上游快、下游慢:日志、传感器、网络流 → buffer(64, SUSPEND)。
-
只关心最新:搜索框联想、进度流 → conflate() 或 buffer(1, DROP_OLDEST)。
-
限制突发 + 保持近实时:buffer(n, DROP_OLDEST)(比如 n=8/16)。
与flowOn的关系
- flowOn(Dispatchers.IO) 自带一个缓冲边界(类似 buffer(Channel.BUFFERED)),使上下文切换处天然解耦。
- 需要更深队列时,可在 flowOn 之后再加一个 buffer(k) 增大吞吐。
scss
upstream
.map { ioHeavy() }
.flowOn(Dispatchers.IO) // 这里已形成一个缓冲边界
.buffer(32) // 再加一段队列
.collect { ui() }
放置位置(很关键)
把 buffer() 放在慢操作之前,切开阻塞链条:
scss
flow
.map { fast() }
.buffer(64) // 切断:避免后面的慢步骤"顶住"前面
.map { slow() }
.collect { consume() }
与 SharedFlow/StateFlow
- 它们自身有 replay/extraBufferCapacity/onBufferOverflow 。若再套 buffer(),会再多一层队列,只有在确实需要二级缓冲/隔离时才加。
小示例:对比分离前后
scss
val f = flow {
repeat(100) { i ->
emit(i) // 生产很快
delay(10)
}
}
// 无 buffer:下游慢会直接"顶住"上游
f.collect { delay(50); println(it) }
// 有 buffer:先排队,上游更流畅(满了再按策略处理)
f.buffer(32, onBufferOverflow = BufferOverflow.SUSPEND)
.collect { delay(50); println(it) }
选择容量的小经验
-
先用默认(≈64),观察队列积压 与内存(可埋点 length/max)。
-
若突发明显且不想丢数据:升大一点,如 128/256。
-
只要最新:conflate() 或 buffer(1, DROP_OLDEST)。
一句话总结
buffer 用"队列"把快上游与慢下游隔离;SUSPEND 保正确性,DROP_OLDEST 保实时性;把 buffer 放在慢步骤之前最有效。