目录
一、协程构建器 (Coroutine Builders)
launch:创建一个新的协程,并返回一个Job,代表协程本身。它不返回任何结果,适用于“发后即忘”的场景。async:创建一个新的协程,返回一个Deferred<T>对象(Job的子接口)。通过在其上调用.await(),可以挂起并获取最终返回的结果。常用于并发执行任务。runBlocking:创建一个新的协程并阻塞当前线程,直到其内部所有逻辑执行完毕。它主要用于桥接普通阻塞代码和挂起风格代码(如在main函数或测试中),日常Android开发中应避免使用。
launch 和 async 必须通过 CoroutineScope 才能调用,是其扩展函数。
二、构造参数/协程上下文(CoroutineContext)
一个协程对应一个CoroutineContext
官方出处:https://developer.android.google.cn/kotlin/coroutines/coroutines-adv?hl=zh-cn
CoroutineContext 使用以下元素集定义协程的行为:
Job:控制协程的生命周期。CoroutineDispatcher:将工作分派到适当的线程。CoroutineName:协程的名称,可用于调试。CoroutineExceptionHandler:处理未捕获的异常。
>>>上下文元素的分类
1.调度器(CoroutineDispatcher)
决定协程在哪个线程或线程池上执行。主要类型有:
Dispatchers.Main:主线程,用于UI操作。Dispatchers.IO:适合执行磁盘或网络I/O操作(如文件读写、网络请求)。Dispatchers.Default:适合执行CPU密集型任务(如列表排序、JSON解析)。Dispatchers.Unconfined:不限定线程,在第一个挂起点之前运行在当前线程,之后由调用resume的线程决定(极少使用)。
2.名称(CoroutineName)
3.异常处理器(CoroutineExceptionHandler )
4....其它任何CoroutineContext的子类
>>>上下文元素的传入方式
1.方式一:直接传入单个元素
fun main() = runBlocking<Unit> {
// 1. 传入调度器
launch(Dispatchers.IO) {
println("IO调度器: ${Thread.currentThread().name}")
}
// 2. 传入协程名称
launch(CoroutineName("MyCoroutine")) {
println("协程名称: ${coroutineContext[CoroutineName]?.name}")
}
// 3. 传入 Job
val parentJob = Job()
launch(parentJob) {
println("使用自定义 Job")
}
// 4. 传入异常处理器
val handler = CoroutineExceptionHandler { _, throwable ->
println("异常: ${throwable.message}")
}
launch(handler) {
throw RuntimeException("出错了")
}
delay(100)
}
2.方式二:使用 + 组合多个元素(最常用)
fun main() = runBlocking<Unit> {
// 组合多个元素
launch(Dispatchers.IO + CoroutineName("组合协程")) {
println("调度器: ${coroutineContext[CoroutineDispatcher]}")
println("名称: ${coroutineContext[CoroutineName]?.name}")
}
// 组合更多元素
val handler = CoroutineExceptionHandler { _, _ -> }
launch(Dispatchers.Default + CoroutineName("完整") + handler + Job()) {
println("所有元素都存在")
}
}
3.方式三:自定义CoroutineScope
自定义CoroutineScope可以传入上下文参数,launch如果不传入上下文参数则会继承CoroutineScope的上下文。
与launch上下文的关系
launch 就像是一个"上下文继承器",它把 Scope 的上下文作为"默认值",把传入的上下文作为"覆盖值",合并后作为新协程的运行环境。
... (还有很多其它传入方式)
>>>上下文元素的获取
// 统一的获取语法
coroutineContext[元素类型] // 推荐
coroutineContext[元素类型.Key] // 显式使用 Key
启动模式 (CoroutineStart)
>>>上下文切换(重点!!!)
withContext 的核心作用就是切换协程的上下文,特别是切换线程。
重点:withContext不会创建新协程,会“阻塞”当前协程。
| 特性 | withContext |
launch |
|---|---|---|
| 是否创建新协程 | ❌ 否 | ✅ 是 |
| 是否挂起 | ✅ 是 | ✅ 是 |
| 返回结果 | ✅ 返回 T | ❌ 返回 Job |
| 异常传播 | 直接抛出 | 传播给父协程 |
| 父子关系 | 当前协程的子任务 | 新协程 |
-
切换线程
fun main() = runBlocking { println("① 主线程: ${Thread.currentThread().name}") withContext(Dispatchers.IO) { println("② 切换到IO线程: ${Thread.currentThread().name}") delay(100) } println("③ 自动切回原线程: ${Thread.currentThread().name}") } -
返回值
| 特性 | withContext |
async |
|---|---|---|
| 返回值类型 | 直接返回 T | 返回 Deferred |
| 是否需要 await | 不需要 | 需要 await() |
| 执行时机 | 立即执行,挂起等待 | 立即执行,可稍后 await |
| 适用场景 | 需要结果并等待 | 可以稍后获取结果 |
三、启动模式 (CoroutineStart)
DEFAULT:立即调度执行协程,随时可以取消。LAZY:懒加载模式,只有在需要时(如调用Job.start()、Job.join()或Deferred.await())才会开始执行。ATOMIC:类似于DEFAULT,但在开始运行前不能被取消。UNDISPATCHED:协程创建后,立即在当前线程执行,直到遇到第一个挂起点。
0 条评论