第2章 Kotlin协程基础回顾:协程作用域、挂起函数、调度器
协程是 Android 中处理异步操作的标准方案,WorkManager 从 2.6.0 版本起原生支持协程。要在 WorkManager 中高效使用协程,必须掌握三个核心概念:协程作用域(管理协程生命周期)、挂起函数(非阻塞等待)和调度器(控制线程池)。本章快速回顾这些基础,聚焦于后续实战中频繁用到的模式。
2.1 协程作用域 (CoroutineScope)
每个协程都必须在某个 CoroutineScope 中启动,作用域决定了协程的存活周期。Android 中常用的作用域有:
| 作用域 | 生命周期 | 典型用途 | 自动取消 |
|---|---|---|---|
GlobalScope | 进程级别 | 一些全局常驻任务(极少使用) | 否 |
coroutineScope { } | 内部所有子协程完成 | 挂起函数内并发 | 由父协程控制 |
supervisorScope { } | 内部子协程独立失败 | 一个任务失败不影响其他 | 由父协程控制 |
viewModelScope (AndroidX) | ViewModel 清除时 | ViewModel 内异步 | 自动 |
lifecycleScope (AndroidX) | Lifecycle 结束时 | Activity/Fragment 内协程 | 自动 |
Worker 内部使用 suspend fun doWork(),它已经运行在协程上下文中,由 WorkManager 提供作用域;不要在 Worker 内自己创建 GlobalScope。2.2 挂起函数 (Suspend Functions)
挂起函数是协程的核心,可以暂停执行而不阻塞线程。声明使用 suspend 关键字:
suspend fun fetchData(): String {
// 模拟网络请求
delay(1000)
return "Data loaded"
}
挂起函数只能在 协程 或 另一个挂起函数 中调用。常用内置挂起函数:delay()、withContext()、yield() 等。
suspend fun processImage(input: Bitmap): Bitmap = withContext(Dispatchers.Default) {
// 密集计算
input.copy(Bitmap.Config.ARGB_8888, false)
}2.3 调度器 (Dispatchers)
调度器决定协程在哪个线程池中执行。常用调度器:
| 调度器 | 线程池 | 典型场景 |
|---|---|---|
Dispatchers.Default | CPU 密集型 — 线程数 = 核心数 + 1 | 图像处理、解析 JSON |
Dispatchers.IO | IO 密集型 — 线程数可能很大 | 网络请求、文件读写、数据库操作 |
Dispatchers.Main | Android 主线程 | UI 更新、点击回调 |
Dispatchers.Unconfined | 调用线程(不切换) | 测试或不需要特定线程时 |
切换调度器使用 withContext,如 withContext(Dispatchers.IO) { ... }。在 WorkManager 中,默认的 doWork() 运行在 Dispatchers.Default 上,但可以通过 withContext(Dispatchers.IO) 来处理 I/O 任务。
2.4 协程构建器
| 构建器 | 返回 | 失败传播 | 典型用法 |
|---|---|---|---|
launch | Job | 向父协程抛出 | “发起并忘记” |
async | Deferred<T> | 仅在 await() 时抛出 | 并发获取多个结果 |
runBlocking | 阻塞调用线程 | 直接抛出 | 测试 / 桥接协程与非协程 |
在 WorkManager 的 doWork() 中,通常只需要挂起函数,不需要手动调用这些构建器;但如果需要并发执行多个任务,可以使用 coroutineScope 包裹 async。
2.5 结构化并发
结构化并发保证:父协程取消时所有子协程自动取消;子协程异常会按作用域规则传播。这是 Android 中避免泄漏和异常崩溃的关键。viewModelScope 和 lifecycleScope 已经内置了结构化并发。在 WorkManager 中,doWork() 本身就是一个挂起函数,其结构化由 WorkManager 内部管理,你只需关注 withContext 和自定义挂起函数的正确性。
GlobalScope.launch 或合适的生命周期作用域启动协程,再调用挂起函数。