Android面试冲击附答案(八)————Flow
一、面试题与答案
1. Flow 是什么?有什么特性?和普通挂起函数区别?
Flow是 Kotlin 协程体系中的异步数据流抽象。- 特性:可组合操作符、背压支持、取消传播、上下文透明。
- 与普通
suspend区别:suspend通常返回单次结果;Flow表示0~N 次连续数据。
2. 什么是冷流和热流?
- 冷流(如
flow {}):订阅才生产,一般一对一。 - 热流(如
SharedFlow/StateFlow):可独立于订阅持续存在,一对多广播。
3. SharedFlow、StateFlow 区别和场景?
SharedFlow:事件流,无必须初值,可配置replay,适合一次性事件/广播。StateFlow:状态流,必须初值,replay=1且状态去重,适合 UI 状态。
4. Flow 上下文保留(Context Preservation)是什么?为什么 flow{} 里不能随意 withContext?
flow {}的执行上下文应保持透明,通常由收集端决定。- 上下文切换建议用
flowOn(作用于上游),而不是在flow {}内随意withContext破坏语义。
5. Flow 异常透明性(Exception Transparency)是什么?
上游异常应由下游显式处理,避免静默吞异常,保证流的可预测性。
常用:catch {}、onCompletion {}、外层 try-catch。
6. flowOf/asFlow/flow/channelFlow 区别?
flowOf():固定值快速建流。asFlow():集合/序列转 Flow。flow {}:最常用,顺序发射。channelFlow {}:支持多协程并发发送,适合多源并发场景。
7. flowOn() 作用?影响上游还是下游?
flowOn 只影响上游执行上下文,不影响下游 collect 所在上下文。
8. buffer/conflate/collectLatest 区别与背压处理?
buffer:缓存排队,尽量不丢数据。conflate:只保留最新值,丢旧值。collectLatest:新值到来时取消旧消费任务。
9. buffer() 和 SharedFlow.extraBufferCapacity 区别?
buffer():冷流链路中的临时缓冲。extraBufferCapacity:SharedFlow自身缓冲配置,生命周期随热流存在。
10. map/filter/transform/flatMapConcat/flatMapMerge/flatMapLatest 区别?
map转换;filter过滤;transform可灵活多次发射。flatMapConcat串行有序。flatMapMerge并发,结果可乱序。flatMapLatest保留最新,取消旧任务。
11. catch() 能捕获上游还是下游异常?
catch 主要捕获上游异常;下游 collect {} 内业务异常通常需外层自己处理。
12. Flow 常见副作用操作符有哪些?
onStart、onEach、onCompletion、onEmpty 等。
它们通常不改变主数据,只做日志、埋点、loading、清理等副作用。
13. retry() 和 retryWhen() 区别?
retry():简单重试,灵活度低。retryWhen():可按异常类型、次数、延迟策略(含退避)自定义。
14. zip() 和 combine() 区别?
zip:一一配对。combine:任一上游更新就与另一个“最新值”组合。
15. stateIn()/shareIn() 与 SharingStarted 三策略?
stateIn/shareIn:把冷流热化。Eagerly:立即启动。Lazily:首个订阅时启动。WhileSubscribed:有订阅才运行,无订阅可停止,UI 场景常用。
16. SharedFlow 参数 replay / extraBufferCapacity / onBufferOverflow 作用?
replay:新订阅者可回放条数。extraBufferCapacity:额外缓冲。onBufferOverflow:溢出策略(挂起/丢最旧/丢最新)。
17. emit() 和 tryEmit() 区别?
emit():挂起发送,必要时等待。tryEmit():非挂起,失败返回false,适合不阻塞发送端场景。
18. SharedFlow 默认配置多消费者问题?
默认无缓冲时,慢消费者可能反压发送端,导致发送端性能问题。多消费者通常需合理配置缓冲与溢出策略。
19. subscriptionCount 有什么用?
表示活跃订阅数量,可用于按订阅数决定是否生产数据或管理资源启停。
20. resetReplayCache() 有什么作用?
清空 SharedFlow 的 replay 缓存,影响后续新订阅者读取到的历史事件。
21. SharedFlow never completes 是什么意思?
SharedFlow 作为热流通常不会自然终止,通常随作用域结束而结束。
22. StateFlow 为什么必须有初始值?
因为它表达“当前状态”,语义上必须始终可读到一个值。
23. StateFlow.value 和 collect 区别?
.value:同步读取当前快照。collect:异步持续监听后续变化。
24. StateFlow 重复 emit 相同值会通知吗?
通常不会。StateFlow 具备类似 distinctUntilChanged 的状态去重语义。
25. compareAndSet() 是什么?
MutableStateFlow.compareAndSet 提供原子更新语义,适合并发安全状态更新。
26. Android 中收集 Flow 为什么不能只用 lifecycleScope.launch?
仅 launch 可能在不可见状态仍收集;应配合 repeatOnLifecycle 按生命周期自动启停。
27. repeatOnLifecycle / flowWithLifecycle / launchWhenX 区别?
repeatOnLifecycle:块级控制,离开状态会取消并重建,官方首选。flowWithLifecycle:单 Flow 生命周期感知封装。launchWhenX:挂起恢复模型,资源释放不如前两者彻底。
28. Flow 取消后,正在执行的 emit 会怎样?
取消后后续挂起点(含 emit)会抛 CancellationException 并终止后续链路;onCompletion 仍会执行清理。
29. channelFlow 和 flow 区别?什么时候必须用 channelFlow?
flow:单协程串行发射。channelFlow:可多协程并发发送。
必须用 channelFlow 的典型场景:多源并发生产、回调桥接、多线程发送。
30. Flow 是线程安全的吗?为什么 FlowCollector 非线程安全?
Flow 作为声明式流模型可线程切换,但 flow {} 中 emit 默认要求顺序语义,FlowCollector 非并发发送模型;并发发送应转 channelFlow。
31. Flow 和 Channel 区别?
Flow:偏声明式数据流处理,操作符丰富。Channel:偏消息管道,手动收发、关闭与背压控制更底层。
32. 如何实现防抖和节流?
- 防抖:
debounce()。 - 节流:Flow 官方无统一内建
throttleFirst,通常自定义实现。
二、Flow原理
1) 角色说明
| 角色 | 职责 | 典型对象 |
|---|---|---|
| 生产者 | 发射数据 | flow { emit(...) } |
| 中间操作符 | 转换/过滤/合并 | map/filter/flatMap/combine |
| 收集者 | 消费数据 | collect/collectLatest |
| 上下文切换器 | 调整上游线程 | flowOn |
| 热化容器 | 多订阅共享 | stateIn/shareIn/SharedFlow/StateFlow |
2) 冷流与热流关系图
冷流 Flow
collect() 才启动
|
+--> 每个订阅者独立执行上游
热流 SharedFlow/StateFlow
创建后可持续存在
|
+--> 多订阅者共享同一数据源
3) 执行链路图
emit --> map --> filter --> buffer/conflate --> collect
| |
数据变换 背压调节
4) 高频对应关系
| 需求 | 推荐方案 | 说明 |
|---|---|---|
| UI 状态 | StateFlow | 有初值、状态语义清晰 |
| 一次性事件 | SharedFlow | 可配置 replay/缓冲 |
| 生命周期收集 | repeatOnLifecycle | 避免后台无效收集 |
| 上游切线程 | flowOn(IO) | 仅影响上游 |
| 高频输入搜索 | debounce + flatMapLatest | 自动淘汰旧请求 |
| 多源并发发射 | channelFlow | 并发安全发送 |