Kotlin Flow 讲清楚:Flow、StateFlow、SharedFlow 到底该怎么用

不晚
不晚
发布时间:2026-08-23| 138人浏览过
原创

前言

Kotlin Flow 在 Android 里几乎已经是异步数据处理的默认选项,但真正落到项目中,很多人最容易混淆的还是三件事:普通 Flow、StateFlow、SharedFlow 各自该管什么,以及它们在 UI 层该怎么收集才不踩生命周期问题。下面就按类型选择、使用场景和界面收集这三条线拆开讲清楚。

Kotlin Flow 已经成为 Android 里处理异步数据流的主流方案,它建立在 Kotlin 协程之上,常被用来替代部分 RxJava 和 LiveData 场景。很多初学者真正卡住的地方,不是会不会写 collect,而是分不清什么时候该用普通 Flow,什么时候该换成 StateFlow 或 SharedFlow。

这篇文章按“数据从哪里来、经过什么处理、最终怎么安全落到 UI”这条线来梳理。你可以据此判断三种 Flow 类型的职责边界,也能顺手把生命周期收集和常用操作符放到合适的位置。

先理解 Flow 的数据管道模型

把 Flow 看成一条异步数据管道会更直观,整个过程通常可以分成三段:

  • 上游(Upstream):负责生产数据,例如数据库读取、网络请求。
  • 中游(Intermediaries):负责处理数据,例如用 filter 过滤、用 map 转换。
  • 下游(Downstream):负责接收和消费数据,例如更新 UI。

这个划分的价值在于:当你排查性能、线程切换或生命周期问题时,能很快判断问题出在“数据源”“处理中间层”还是“界面消费层”。

冷流与热流,到底差在哪里

Flow 体系里最核心的一组概念,就是冷流和热流。

对比 Kotlin 冷流与热流,以及 Flow、StateFlow、SharedFlow 的典型职责
Kotlin Flow 类型选择图用对比图把冷流与热流的触发方式、共享方式和典型用途放在一张图里,方便快速判断该选哪一类。
特性Cold Flow(普通 Flow)Hot Flow(StateFlow / SharedFlow)
激活时机只有被收集(collect)时才开始运行无论有无收集者,它都在内存中活跃,行为上类似 LiveData
数据生产一对一(Unicast),每个收集者都会重新触发一次数据生产流程一对多(Multicast),多个收集者共享同一个数据源
典型场景耗时数据库读取、文件下载、简单异步转换UI 状态管理(StateFlow)、一次性事件(SharedFlow)

如果你只记一个判断标准,可以记这一句:需要“每次收集都重新执行”的,用普通 Flow;需要“值或事件在内存里持续存在并可被多个观察者共享”的,用热流。

Flow、StateFlow、SharedFlow 分别适合什么场景

普通 Flow:适合按需执行的异步任务

普通 Flow 是最基础的类型,适合执行耗时任务,并按顺序发射多个值。它的特点是“没人收集就不运行”,因此非常适合那些明确由下游触发的工作流。

// 例子:倒计时
fun countdownFlow(): Flow<Int> = flow {
    for (i in 5 downTo 0) {
        emit(i) // 发射数据
        delay(1000) // 挂起函数,不阻塞线程
    }
}

像倒计时、一次数据库查询、一次文件下载这类逻辑,用普通 Flow 很顺手,因为每次进入收集流程都会完整执行一遍上游逻辑。

StateFlow:适合持续保存 UI 状态

StateFlow 可以看作是 LiveData 的现代替代品,特别适合 ViewModel 中的状态管理。

  • 特点:始终持有最新的一个状态值。
  • 收集行为:新的收集者加入时,会立刻收到当前最新值。
  • 使用场景:保存 UI 状态,例如 Loading、Success、Error。
  • 和 LiveData 的区别:StateFlow 必须有初始值,同时完整支持协程操作符。
// ViewModel 中
private val _uiState = MutableStateFlow("Initial State")
val uiState: StateFlow<String> = _uiState.asStateFlow()

// 更新状态
_uiState.value = "New State"

如果一个值代表“当前界面此刻应该显示什么”,那它大概率应该是 StateFlow,而不是普通 Flow。

SharedFlow:适合一次性事件分发

SharedFlow 更适合发送一次性事件,也就是那些“发生过就算处理完”的消息。

  • 特点:用于发送一次性事件(One-off events)。
  • 默认行为:默认不保留旧数据,除非额外配置 replay。
  • 收集行为:新收集者不会收到过去已经发出的事件。
  • 典型场景:导航跳转、弹出 Toast、显示 Snackbar、服务器推送消息。
// ViewModel 中
private val _events = MutableSharedFlow<String>()
val events: SharedFlow<String> = _events.asSharedFlow()

// 发送事件 (suspend function)
_events.emit("Show Toast")

一个常见误区是把 Toast、页面跳转这类事件塞进 StateFlow。这样很容易在界面重建后重复消费旧值。遇到这类“一次触发、一次处理”的需求,通常应该优先考虑 SharedFlow。

在 UI 层怎么安全收集 Flow

Flow 好用,但前提是收集方式要和生命周期对齐。否则页面退到后台后仍在收集,就很容易造成资源浪费,甚至引发界面更新时机错误。

展示 Activity、Fragment 与 Compose 中安全收集 Flow 的方式
Flow 的生命周期安全收集这一张聚焦 UI 层收集方式,突出 Views 与 Compose 的推荐写法。

Activity / Fragment:用 repeatOnLifecycle

在传统 Views 体系里,推荐使用 repeatOnLifecycle。它会在生命周期达到指定状态时开始收集,在状态低于该值时自动取消协程。

最常见的写法是使用 Lifecycle.State.STARTED:进入前台开始收集,进入 STOPPED 后停止接收数据。

// 在 Activity 或 Fragment 中
lifecycleScope.launch {
    // 只有当生命周期至少是 STARTED 时才执行块内代码
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        // 这两个流会并行收集
        launch {
            viewModel.uiState.collect { state ->
                // 更新 UI
            }
        }
        launch {
            viewModel.events.collect { event ->
                // 处理事件
            }
        }
    }
}

这里把 uiState 和 events 分别放进两个 launch 中并行收集,是非常常见也非常实用的模式。

Compose:用 collectAsStateWithLifecycle()

在 Jetpack Compose 中,通常直接使用 collectAsStateWithLifecycle()。它会把 Flow 转成 Compose 可用的 State,同时感知生命周期,减少手动处理的负担。

@Composable
fun MyScreen(viewModel: MyViewModel) {
    // 需要引入依赖: androidx.lifecycle:lifecycle-runtime-compose
    val state by viewModel.uiState.collectAsStateWithLifecycle()

    Text(text = state)
}

如果页面展示的是持续变化的状态,Compose + collectAsStateWithLifecycle() 基本就是当前最顺手的组合。

常用操作符该怎么理解

Flow 的强大之处,不只在于发数据,还在于可以沿着数据流一路加工。下面这些操作符最常见:

转换类:改造数据结构

  • map:做数据映射。
  • transform:做更灵活的自定义转换。

过滤类:减少无效更新

  • filter:按条件过滤。
  • debounce:防抖,搜索框场景很常见。
  • distinctUntilChanged:过滤重复值;StateFlow 默认自带这一特性。

组合类:处理多个数据源

  • combine:合并多个流。
  • zip:按配对方式组合多个流。
  • flatMapLatest:切换流,适合“搜索词变化时取消旧请求、发起新请求”这类场景。

异常处理与线程切换

  • catch:捕获上游异常。
  • flowOn(Dispatchers.IO):指定上游代码在 IO 线程执行。

一个实用理解方式是:catch 和 flowOn 往往都更偏“上游控制”;UI 层应该尽量专注于收集结果,而不是承担过多数据加工责任。

最后总结:三种 Flow 类型怎么选

  • 需要按需触发、每次收集都重新执行任务:选普通 Flow。
  • 需要持续保存并分发最新 UI 状态:选 StateFlow。
  • 需要发送导航、Toast、Snackbar 这类一次性事件:选 SharedFlow。

如果再配合 repeatOnLifecycle 或 collectAsStateWithLifecycle() 做生命周期感知收集,Flow 在 Android 项目里的可维护性会明显高于很多早期的异步方案。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。