一、协程解决什么问题
协程不是为了让代码“看起来更高级”,而是为了用同步写法组织异步任务。网络请求、数据库读写、文件 IO、定时任务都可以放进协程里,让代码更容易串联和取消。
传统回调写法容易出现几个问题:
- 多层回调嵌套。
- 错误处理分散。
- 页面销毁后任务还在执行。
- 多个异步任务之间的生命周期不清楚。
协程的价值是把这些异步任务放到明确的作用域中管理。
二、结构化并发
结构化并发的核心是:协程必须属于某个作用域,父协程结束时,子协程也应该一起结束。
1 | suspend fun loadHomeData(): HomeData = coroutineScope { |
这里 banner 和 articles 是并发执行的,但它们都受 coroutineScope 管理。如果其中一个失败,整个作用域会失败,另一个任务也会被取消。
这比随手使用全局协程更可靠:
1 | GlobalScope.launch { |
GlobalScope 的生命周期太长,容易造成页面退出后任务继续执行。
三、常用作用域
Android 中常见作用域:
viewModelScope:跟随 ViewModel 生命周期。lifecycleScope:跟随 Activity 或 Fragment 生命周期。rememberCoroutineScope:Compose 中跟随当前组合生命周期。
一般建议:
- 页面业务请求放在
viewModelScope。 - 和界面生命周期强相关的动作放在
lifecycleScope。 - Compose 点击事件中触发 Snackbar、滚动等 UI 动作用
rememberCoroutineScope。
示例:
1 | class UserViewModel : ViewModel() { |
四、调度器选择
协程调度器决定任务在哪类线程执行:
Dispatchers.Main:主线程,更新 UI。Dispatchers.IO:网络、文件、数据库等 IO 操作。Dispatchers.Default:CPU 密集型计算。Dispatchers.Unconfined:特殊场景,不建议日常业务使用。
Repository 中可以显式切换到 IO:
1 | class UserRepository( |
ViewModel 不需要关心底层线程细节,只负责调用业务方法和更新状态。
五、异常处理
协程异常处理要先区分两种场景:当前任务可以失败,还是失败后要影响整个并发流程。
普通串行任务可以用 try/catch:
1 | viewModelScope.launch { |
多个任务中,如果希望某个任务失败不影响其他任务,可以使用 supervisorScope:
1 | suspend fun loadDashboard(): Dashboard = supervisorScope { |
这种方式适合首页、工作台等多个模块相互独立的页面。
六、取消要可响应
协程取消是协作式的。挂起函数通常能响应取消,但如果自己写了长循环,就要主动检查。
1 | suspend fun parseLargeFile(lines: List<String>) = withContext(Dispatchers.Default) { |
不要吞掉取消异常:
1 | try { |
更稳妥的写法:
1 | catch (e: CancellationException) { |
七、Flow 和协程
Flow 适合表达持续变化的数据,例如数据库观察、搜索关键词、分页状态。
1 | val articles: StateFlow<List<Article>> = |
搜索输入可以用防抖:
1 | val result = keywordFlow |
flatMapLatest 的特点是新关键词到来时取消旧搜索,很适合搜索框场景。
八、实践建议
协程代码建议遵守几个习惯:
- 不在业务代码里使用
GlobalScope。 - Repository 提供
suspend或FlowAPI。 - ViewModel 负责启动协程。
- 长耗时任务放到合适的 Dispatcher。
- 不吞掉
CancellationException。 - 并发任务优先使用
coroutineScope或supervisorScope。
九、小结
协程写得好不好,关键不在于用了多少 API,而在于生命周期、异常和取消是否清晰。
可以用一句话记住:任务属于作用域,失败要有边界,取消必须尊重。
- 本文链接: https://blog.hansong.icu/2026/06/22/Kotlin_Coroutines_Structured_Concurrency/
- 版权声明: 本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。