一、数据流基本介绍

基于协程的数据流处理框架

🎯 核心概念:异步数据序列

Flow 是一种可以异步发出多个值的数据流,这使它区别于仅返回单个值的挂起函数。它主要由三个实体构成:

  • 提供方:负责生成并添加数据到流中。
  • 中介:(可选)负责修改发送到流中的值或流本身。
  • 使用方:负责接收和使用流中的值。

🏗️ Flow 的基石:冷流与热流

理解 冷流热流 的区别至关重要:

  • 冷流 (如 flow 构建器)无状态每次 当有使用方(调用 collect)时,提供方的代码就会重新执行。它不存储数据。
  • 热流 (如 StateFlow/SharedFlow)有状态,独立于收集操作而存在,数据会被缓存。多个使用方可以共享同一个热流实例并接收数据,且收集不会触发提供方代码。

主要 API 与用法全景图

1. 创建 Flow:从源头开始

  • flow { ... } 构建器:最常用的方式。在代码块中通过 emit(value) 发出数据。
  • flowOf(...):基于一组固定的值创建 Flow。
  • .asFlow():将各种集合(如 ListSet)或序列转换为 Flow。
  • callbackFlow { ... }关键工具。用于将基于回调的 API(如 Firebase Listener)转换为 Flow。需在 awaitClose { } 中清理资源。

2. 修饰 Flow:中间运算符(懒加载)

这些运算符应用于 Flow 后,会返回一个新的 Flow,并定义数据转换的逻辑,但不会立即执行,直到有终端运算符开始收集。

  • 转换maptransform(可多次 emit)。
  • 过滤filtertakedropdistinctUntilChanged
  • 组合
    • zip:将两个 Flow 严格配对,以较短的 Flow 为准结束。
    • combine:当 Flow 中任意一个有新值时,用各自的最新值进行组合。
  • 处理背压与性能
    • buffer():为 Flow 添加缓冲区,允许生产者与消费者并行运行,优化整体执行时间。
    • conflate():仅保留最新的值,当消费者处理速度慢时,中间值会被丢弃。
    • collectLatest:当有新值发出时,如果旧值还未处理完,则取消旧值的处理。
  • 线程切换flowOn(Dispatcher):改变上游flowOn之前的部分)执行的协程调度器。
  • 错误处理catch { ... }:捕获上游异常,可 emit 备用值或重新抛出。
  • 副作用onEachonStartonCompletion:用于监听 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,避免资源浪费和内存泄漏。

💡 核心要点与最佳实践

  1. 冷热流选择:对于每次收集都需要独立计算的场景,用冷流;对于需要共享状态或事件的场景,用热流StateFlow/SharedFlow)。
  2. 异常处理:始终在 Flow 链的适当位置使用 catch 运算符来处理异常,并考虑是否要发出一个备用值。
  3. 线程管理:使用 flowOn 将耗时操作(如网络请求、数据库读写)切换到后台调度器,保持界面线程流畅。
  4. 背压处理:当生产者速度可能快于消费者时,根据业务需求选择 bufferconflatecollectLatest
  5. 资源释放:在自定义的 callbackFlow 中,务必在 awaitClose { } 中清理回调监听器,防止内存泄漏。

二、repeatOnLifecycle 或 shareIn

🆚 核心区别与联系

通过下表可以更清晰地理解两者的定位:

对比维度 repeatOnLifecycle shareIn
主要角色 生命周期安全的收集器 数据源共享转换器
使用位置 Activity / Fragment (界面层) ViewModel / 数据层
核心目的 根据生命周期自动开始/停止收集,节省资源,避免崩溃。 将冷流变为热流,共享数据源,避免重复计算。
操作对象 任何 Flow 通常是冷流
产出结果 无,它是收集行为本身 一个可以被多个收集者共享的热流 (SharedFlow)
关键参数 Lifecycle.State (如 STARTED) started (启动策略, 如 WhileSubscribed) 和 replay

💡 协同工作模式

它们俩经常在架构中协同工作

  1. ViewModel,您使用 shareInstateIn 将一个底层的冷流转换为热流(如 StateFlow),并暴露给界面层。
  2. Fragment,您使用 repeatOnLifecycle 来安全地收集这个暴露出来的热流,确保界面在可见时才更新,不可见时自动停止收集。

这种模式既保证了数据源的共享和效率,又保证了界面层收集的安全性,是官方推荐的架构实践。


0 条评论

发表回复

您的电子邮箱地址不会被公开。