目录
一、数据流基本介绍
基于协程的数据流处理框架
🎯 核心概念:异步数据序列
Flow 是一种可以异步发出多个值的数据流,这使它区别于仅返回单个值的挂起函数。它主要由三个实体构成:
- 提供方:负责生成并添加数据到流中。
- 中介:(可选)负责修改发送到流中的值或流本身。
- 使用方:负责接收和使用流中的值。
🏗️ Flow 的基石:冷流与热流
理解 冷流 和 热流 的区别至关重要:
- 冷流 (如
flow构建器):无状态,每次 当有使用方(调用collect)时,提供方的代码就会重新执行。它不存储数据。 - 热流 (如
StateFlow/SharedFlow):有状态,独立于收集操作而存在,数据会被缓存。多个使用方可以共享同一个热流实例并接收数据,且收集不会触发提供方代码。
主要 API 与用法全景图
1. 创建 Flow:从源头开始
flow { ... }构建器:最常用的方式。在代码块中通过emit(value)发出数据。flowOf(...):基于一组固定的值创建 Flow。.asFlow():将各种集合(如List、Set)或序列转换为 Flow。callbackFlow { ... }:关键工具。用于将基于回调的 API(如 Firebase Listener)转换为 Flow。需在awaitClose { }中清理资源。
2. 修饰 Flow:中间运算符(懒加载)
这些运算符应用于 Flow 后,会返回一个新的 Flow,并定义数据转换的逻辑,但不会立即执行,直到有终端运算符开始收集。
- 转换:
map、transform(可多次emit)。 - 过滤:
filter、take、drop、distinctUntilChanged。 - 组合:
zip:将两个 Flow 严格配对,以较短的 Flow 为准结束。combine:当 Flow 中任意一个有新值时,用各自的最新值进行组合。
- 处理背压与性能:
buffer():为 Flow 添加缓冲区,允许生产者与消费者并行运行,优化整体执行时间。conflate():仅保留最新的值,当消费者处理速度慢时,中间值会被丢弃。collectLatest:当有新值发出时,如果旧值还未处理完,则取消旧值的处理。
- 线程切换:
flowOn(Dispatcher):改变上游(flowOn之前的部分)执行的协程调度器。 - 错误处理:
catch { ... }:捕获上游异常,可emit备用值或重新抛出。 - 副作用:
onEach、onStart、onCompletion:用于监听 Flow 的生命周期事件,不影响数据本身。
3. 使用 Flow:终端运算符(触发执行)
终端运算符是挂起函数,调用后会启动 Flow 并开始接收数据。
collect { ... }:最基础的,逐个处理每个值。- 转换为其他类型:
toList()、toSet()。 - 获取特定值:
first()、single()。 - 规约操作:
reduce()、fold()。 - 判断与计数:
any()、count()。
🔥 状态与事件:StateFlow 和 SharedFlow
它们是热流,非常适合在 ViewModel 中向界面暴露状态或事件。
StateFlow:状态容器
- 特点:总是持有一个最新状态。新收集器会立即收到当前的最新状态。类似于 LiveData,但需要配合
repeatOnLifecycle在界面层安全收集。 - 使用模式:在 ViewModel 中,通常使用私有的
MutableStateFlow作为后备属性,对外暴露不可变的StateFlow。通过更新其value来发送新状态。 - 转换工具:
.stateIn可以将普通 Flow 或 SharedFlow 转换为 StateFlow,需要指定作用域、初始值和启动策略。
SharedFlow:事件总线
- 特点:是 StateFlow 的泛化版本,可高度配置。适合发送一次性事件(如导航、Snackbar 消息)或需要多个订阅者共享的数据流。
- 核心参数:
replay:为新订阅者重放之前发出的 N 个值。extraBufferCapacity:除了重放缓存外的额外缓冲区大小。onBufferOverflow:缓冲区满时的策略(挂起、丢弃最新、丢弃最旧)。
- 转换工具:
.shareIn可以将普通 Flow 转换为 SharedFlow,需要指定作用域、重放数量和启动策略。
📱 与 Android 架构组件集成
- Room:在 DAO 中,让查询函数返回
Flow<T>,即可在数据库变更时自动收到最新数据。 - ViewModel:
- 使用
viewModelScope启动协程来收集数据源或执行操作。 - 使用
StateFlow暴露界面状态,确保状态在配置变更后依然存活。 - 在界面层使用
repeatOnLifecycle(Lifecycle.State.STARTED)来安全地收集 Flow,避免资源浪费和内存泄漏。
- 使用
💡 核心要点与最佳实践
- 冷热流选择:对于每次收集都需要独立计算的场景,用冷流;对于需要共享状态或事件的场景,用热流(
StateFlow/SharedFlow)。 - 异常处理:始终在 Flow 链的适当位置使用
catch运算符来处理异常,并考虑是否要发出一个备用值。 - 线程管理:使用
flowOn将耗时操作(如网络请求、数据库读写)切换到后台调度器,保持界面线程流畅。 - 背压处理:当生产者速度可能快于消费者时,根据业务需求选择
buffer、conflate或collectLatest。 - 资源释放:在自定义的
callbackFlow中,务必在awaitClose { }中清理回调监听器,防止内存泄漏。
二、repeatOnLifecycle 或 shareIn
🆚 核心区别与联系
通过下表可以更清晰地理解两者的定位:
| 对比维度 | repeatOnLifecycle |
shareIn |
|---|---|---|
| 主要角色 | 生命周期安全的收集器 | 数据源共享转换器 |
| 使用位置 | Activity / Fragment (界面层) |
ViewModel / 数据层 |
| 核心目的 | 根据生命周期自动开始/停止收集,节省资源,避免崩溃。 | 将冷流变为热流,共享数据源,避免重复计算。 |
| 操作对象 | 任何 Flow |
通常是冷流 |
| 产出结果 | 无,它是收集行为本身 | 一个可以被多个收集者共享的热流 (SharedFlow) |
| 关键参数 | Lifecycle.State (如 STARTED) |
started (启动策略, 如 WhileSubscribed) 和 replay |
💡 协同工作模式
它们俩经常在架构中协同工作:
- 在
ViewModel中,您使用shareIn或stateIn将一个底层的冷流转换为热流(如StateFlow),并暴露给界面层。 - 在
Fragment中,您使用repeatOnLifecycle来安全地收集这个暴露出来的热流,确保界面在可见时才更新,不可见时自动停止收集。
这种模式既保证了数据源的共享和效率,又保证了界面层收集的安全性,是官方推荐的架构实践。
0 条评论