第一章 协程与 Flow 概述

1.1 发展背景

协程这个概念早在 60 年代就出现了,但真正在 Android 圈火起来,是 Kotlin 在 1.3 版本把它带进来的。我记得 2019 年第一次用协程时,心里还在嘀咕:这不就是线程换了个马甲吗?

后来仔细一琢磨,还真不一样。协程是跑在用户态的轻量级线程,由编译器帮我们管理挂起和恢复。说白了,你写同步的代码,跑出异步的效果。不用再回调地狱里修修补补了。

Flow 呢?它是 Kotlin 协程生态里专门处理冷数据流的工具。我用 RxJava 好几年,看到 Flow 的第一反应:这不就是 RxJava 的 Observable 吗?但用下来发现,Flow 天生跟协程绑定,结构化并发,取消自动,测试起来也清爽很多。

对比维度传统回调RxJavaKotlin 协程 + Flow
代码可读性嵌套多,难维护链式调用,稍好同步风格,自然
取消机制手动处理Disposable 管理结构化取消,自动
背压支持无内置策略Flow 自带缓冲/合并
测试难度Mock 回调复杂TestScheduler 还行runTest + 虚拟时间,爽

1.2 核心概念

我们先看协程的两个核心:挂起函数和协程作用域。

挂起函数 — 在函数前加 suspend 关键字,它可以在不阻塞线程的前提下挂起当前协程,等结果回来再恢复。你想想看,它不像线程那样阻塞,而是让出执行权,线程继续干别的活。

协程作用域则决定了协程的生命周期。我个人习惯把作用域绑定到 ViewModel 或 Activity 上,比如 viewModelScope。页面销毁,协程自动取消,不会泄露。

什么是 Flow?

Flow 是一个冷流:只有被收集(collect)时才开始发射数据。跟 LiveData 不一样,它默认不在主线程发射,需要我们用 flowOn 指定调度器。

// 一个简单的 Flow 示例
fun fetchUserData(): Flow<String> = flow {
    for (i in 1..3) {
        delay(1000)
        emit("User data chunk $i")
    }
}

嗯,这里要注意:Flow 里的 emit 是挂起函数,不会阻塞线程。我在项目中遇到过,有人把 emit 放到 withContext(Dispatchers.IO) 里,忘了 flowOn,结果数据全在主线程发射了……

1.3 适用场景归纳总结

协程跟 Flow 并不是银弹,但有几个场景用起来是真香:

  • 网络请求串行/并行:用 async/await 或 flatMapConcat,比回调美观太多。
  • 数据流观测:比如定位变化、传感器数据,用 callbackFlow 转成 Flow,再配合 catch 处理异常。
  • UI 层刷新:Flow 的 collectLatest 可以跳过中间值,只处理最新数据。我用它做搜索防抖,省了自定义 debounce 的代码。
  • 单元测试:协程的 runTest 加上 TestDispatcher,能模拟时间快进。我曾经因为异步测试跑得慢,后来全换成虚拟时间,测试套件从 5 分钟缩到 30 秒。
我个人建议:对于常规的异步任务(一次请求、一次数据库查询),直接用 viewModelScope.launch 配合 suspend 函数就够了。只有当你需要连续发射多个值,或者要做数据变换时,才上 Flow。别把 Flow 当成万能工具,它是有代价的(冷流本质,多次 collect 会重复执行)。
避坑指南:我曾经在 RecyclerView 的 Adapter 里直接 collect Flow,结果每次点击刷新都重新创建了一个 Flow 实例,导致内存泄漏。正确做法是把 Flow 存放在 ViewModel 里,用 StateFlow 或者 shareIn 做热流转换。
协程与 Flow 知识体系 协程核心 挂起函数 协程作用域 调度器 (Dispatchers) Flow 基础 冷流 (Cold Stream) flow { } 构建器 collect / collectLatest Flow 高级 背压 (buffer/conflate) 共享 (shareIn/stateIn) 异常处理 (catch) 测试 runTest TestDispatcher 应用场景 网络请求 / 数据库 UI 状态 / 传感器 核心思路:协程解决异步调度,Flow 解决数据流;测试贯穿始终,让代码可验证。

1.4 总结

协程和 Flow 并不是什么新魔法,它们只是帮我们把异步代码的复杂度藏到了语言层面。我个人觉得,理解「挂起」的本质(不是阻塞,而是让出线程)比背 API 更重要。

至于适用场景,记住三点就够了:

  1. 一次请求 → suspend 函数
  2. 多次发射 → Flow
  3. 状态共享 → StateFlow 或 SharedFlow

好了,这章先聊到这儿。接下来在实践环节,我们再一个一个拆开细看。