launch
作用
构建一个协程, 直接返回Job
使用
Kotlin
// lifecycleScope 协程作用域
lifecycleScope.launch { // 启动一个父协程
delay(10000) // 模拟任务耗时
}
源码解析
Kotlin
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
// 组合上下文
val newContext = newCoroutineContext(context)
// 根据启动模式创建协程对象
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
// 启动协程
coroutine.start(start, coroutine, block)
// 返回这个协程对象(它本身就是个 Job)
return coroutine
}
/**
* 协程的"基因合成器",它通过 plus 操作符将父 Scope 的上下文与启动参数合并,
* 并遵循"参数覆盖父辈、默认 Dispatchers.Default 强制补位"的优先级逻辑,
* 合成出子协程最终的协程上下文。
*/
public fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
// 将 Scope 的 context 与 传入的 context 合并
val combined = coroutineContext.foldCopiesForChild() + context
// 注入默认调度器 (Dispatcher)
val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
// 如果 combined 里没有 Dispatchers,则补上 Dispatchers.Default
return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
debug + Dispatchers.Default else debug
}
/**
* StandaloneCoroutine 定义极其简练,
* 主要承担 "异常处理终点站" 这一角色
* 继承关系: StandaloneCoroutine -> AbstractCoroutine -> JobSupport -> Job
*/
private open class StandaloneCoroutine(
parentContext: CoroutineContext,
active: Boolean
) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active) {
/**
* 当协程体抛出异常,状态机会通过 resumeWith(Result.failure)
* 回调到 StandaloneCoroutine, 父类 JobSupport 会调用这个 handleJobException,
*/
override fun handleJobException(exception: Throwable): Boolean {
/**
* 会顺着 context 寻找自定义的 CoroutineExceptionHandler
* 如果自定义了捕获逻辑,就执行自定义的,
* 如果没有, 就抛给 JVM 的Thread.UncaughtExceptionHandler 将App闪退
*/
handleCoroutineException(context, exception)
return true // 告知我已经接受处理这个异常
}
}
// 源码简化版:AbstractCoroutine.kt
public abstract class AbstractCoroutine<in T>(
parentContext: CoroutineContext,
initParentJob: Boolean,
active: Boolean
) : JobSupport(active), Job, Continuation<T> {
init {
// 如果需要初始化父子关系(默认都是 true)
if (initParentJob) initParentJob(parentContext[Job])
}
protected fun initParentJob(parent: Job?) {
if (parent == null) return // 没父亲(如 GlobalScope),直接返回
parent.start() // 确保父亲也是启动状态
// 核心:把"自己"交给"父亲"管理,并拿到一个句柄
val handle = parent.attachChild(this)
parentHandle = handle // 子协程保存这个句柄,用于后续解除绑定
}
}
/**
* JobSupport 类
* 将子协程包装成子节点挂载到父协程自己的 NodeList 双向链表中
*/
public final override fun attachChild(child: ChildJob): ChildHandle {
// 1. 创建一个 ChildHandleNode,它是一个双向链表节点
// 2. 调用 invokeOnCompletion,传入特定的 internal = true 标志
return invokeOnCompletion(
onCancelling = true,
handler = ChildHandleNode(child).asHandler
) as ChildHandle
}
总结
launch 的执行流程大致可以总结如下:
- 组合上下文
- 组合参数上下文和父协程上下文
- 优先级为: 参数上下文(右侧覆盖左侧) > 父协程上下文 > 默认Dispatchers.Default
- 父子绑定
- 在实例化 StandaloneCoroutine 时, 父协程 Job 通过attachChild, 将子协程包装成ChildHandleNode, 并挂载到双向链表NodeList中, 实现 "取消向下传播, 异常向上传播" 的能力
- 分发启动
- 调用协程 start(), 调度器将协程任务包装成Runnable丢入线程池, 线程执行时, 通过状态机的resumeSuspend 开始逻辑执行
- 异常报警
- 若某协程崩溃, StandaloneCoroutine 将立即触发handleJobException, 将任务抛给自定义CoroutineExceptionHandler 或 JVM 的Thread.UncaughtExceptionHandler, 闪退App
- 完结注销
- 任务结束后, 通过 parentHandle.dispose 将自己从父协程链表中删除
async
作用
构建一个带返回值的协程, 返回 Deffered, 它继承自 Job, 它不仅可以管理生命周期, 还能通过调用 await() 挂起并获取 "未来" 结果.
使用
Kotlin
// lifecycleScope 协程作用域
lifecycleScope.launch { // 启动一个父协程
val deferred = lifecycleScope.async {
delay(10000)
"Success" // 返回结果
}
val a = deferred.await() // 挂起并等待结果
Log.d("--", "a=${a}")
}
源码解析
Kotlin
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
// 组合上下文(同 launch)
val newContext = newCoroutineContext(context)
// 创建协程对象:DeferredCoroutine 实例
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
// 启动协程
coroutine.start(start, coroutine, block)
return coroutine
}
/**
* DeferredCoroutine 只实现了 await, 但未实现 handleJobException
* 继承关系: DeferredCoroutine -> AbstractCoroutine -> JobSupport -> Job
*/
private open class DeferredCoroutine<T>(
parentContext: CoroutineContext,
active: Boolean
) : AbstractCoroutine<T>(parentContext, initParentJob = true, active = active), Deferred<T> {
// 调用 await 时进入 JobSupport 的内部逻辑
override suspend fun await(): T = awaitInternal() as T
}
/** await 内部方法 */
internal suspend fun JobSupport.awaitInternal(): Any? {
while (true) {
val state = this.state
// 如果已完成,直接返回结果(或抛出异常)
if (state !is Incomplete) return state.makeQueryState()
// 如果未完成,尝试进入等待状态
if (startAwaitOrJoin(state) >= 0) break
}
// 正式挂起,直到结果就绪被唤醒
return awaitSuspend()
}
private suspend fun awaitSuspend(): Any? = suspendCoroutineUninterceptoredOrReturn { uCont ->
// 包装当前 Continuation 为 AwaitContinuation 节点
val waiter = AwaitContinuation(uCont.intercepted(), this)
// 将这个"等待节点"也挂载到 JobSupport 的双向链表 NodeList 中
val handle = invokeOnCompletion(handler = waiter)
COROUTINE_SUSPENDED
}
总结
async 的底层逻辑大致如下:
- 组合上下文
- 组合参数上下文和父协程上下文
- 优先级为: 参数上下文(右侧覆盖左侧) > 父协程上下文 > 默认Dispatchers.Default
- 父子绑定
- 在实例化 DeferredCoroutine 时, 父协程 Job 通过attachChild, 将子协程包装成ChildHandleNode, 并挂载到双向链表NodeList中, 实现 "取消向下传播, 异常向上传播" 的能力
- 分发启动
- 调用协程 start(), 调度器将协程任务包装成Runnable丢入线程池, 线程执行时, 通过状态机的resumeSuspend 开始逻辑执行
- 挂起等待
- 当开发者调用 await() 时, 如结果已就绪则返回, 结果未就绪, 则将 Continuation 包装成 AwaitContinuation 节点, 然后挂载到 双向链表 NodeList 中
- 结果存入与唤醒
- 当 async 协程逻辑跑完, AbstractCoroutine 内会将结果存入 JobSupport 的状体位, 并遍历NodeList, 找到 AwaitContinuation , 通过其内部持有的 Continuation 执行 resume(result), 唤醒挂起点.
- 完结注销
- 任务结束后, 通过 parentHandle.dispose 将自己从父协程链表中删除
两者区别
相同点
第一: 协程底子相同
虽然两者对应协程类不同, ++launch是StandaloneCoroutine++ , ++async是DeferredCoroutine++, 但都继承自 AbstractCoroutine, 都拥有协程生命周期管理等 AbstractCoroutine 拥有的能力
第二: 上下文组合方式相同
遵循的组合优先级都是: 参数上下文(右侧覆盖左侧) > 父上下文 > 默认协程调度器
第三: 结构化并发相同
都通过 attachChild 将子协程包装成子节点, 挂载到父协程的双向链表 NodeList 中
默认情况下都遵循 "父协程取消, 向下递归子协程取消"、"子协程崩溃, 向上通知父协程取消"
第四: 分发逻辑相同
都依赖 CoroutineDispatcher 进行任务分发, 启动后都会封装成 DispatcherContinuation 丢入线程池排队执行.
不同点
两者区别主要在: 返回值、异常处理机制
第一: 返回值不同
launch 返回Job, 用于执行不需要结果的任务
async 返回 Deferred, 用于执行需要等待异步结果的任务
第二: 异常处理机制不同
launch 协程是StandaloneCoroutine, 它实现了handleJobException, 协程任务如果发生崩溃, 崩溃会即刻向上传播到自定义的 ++CoroutineExceptionHandler++ 或通过 JVM 的 ++Thread.UncaughtExceptionHandler++ 崩溃App.
async 协程是DeferredCoroutine, 未实现handleJobException, 任务如果发生崩溃, 不会立即处理, 而是会等到开发者执行了 Deferred.await() 之后, 异常才会被重新抛出.