android kotlin launch 源码分析

以下是对 Kotlin launch 从入口到 Android 线程调度的完整源码级分析,逐层深入到 kotlinx.coroutines 核心实现。


一、入口层:launch 函数

launch 定义在 Builders.common.kt 中,是 CoroutineScope 的扩展函数:

kotlin

复制代码
// kotlinx.coroutines.Builders.common.kt
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    // 1. 合并上下文
    val newContext = newCoroutineContext(context)
    
    // 2. 根据启动模式创建协程体
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block)
    else
        StandaloneCoroutine(newContext, active = true)
    
    // 3. 启动协程
    coroutine.start(start, coroutine, block)
    
    // 4. 返回 Job,外部可通过它取消或等待
    return coroutine
}

关键洞察launch 只干三件事------合上下文、造协程体、启动它


二、上下文合并:newCoroutineContext

kotlin

复制代码
// kotlinx.coroutines.CoroutineContext.common.kt
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
    // 父 Scope 的 context + 传入的 context,后者覆盖前者同 key 的元素
    val combined = coroutineContext + context
    
    // DEBUG 模式下追加唯一 ID,便于日志追踪
    val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
    
    // 如果没有指定 Dispatcher,默认追加 Dispatchers.Default
    return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
        debug + Dispatchers.Default
    else
        debug
}

上下文合并规则

  • coroutineContext + context:右侧覆盖左侧同 key 的元素(如传入 Dispatchers.Main 会覆盖 Scope 默认的 Dispatchers.IO)。

  • ContinuationInterceptor key 用于存储 Dispatcher,若未设置则默认 Dispatchers.Default


三、协程体创建:StandaloneCoroutine

kotlin

复制代码
// kotlinx.coroutines.Builders.common.kt
private open class StandaloneCoroutine(
    parentContext: CoroutineContext,
    active: Boolean
) : AbstractCoroutine<Unit>(parentContext, initParentJob = true, active = active)

继承链

plain

复制代码
StandaloneCoroutine
    → AbstractCoroutine<Unit>
        → JobSupport          ← 实现 Job 的完整状态机(Active/Completing/Cancelled)
        → Continuation<Unit>  ← 实现 Continuation 接口,作为协程完成的回调

StandaloneCoroutine 同时是 Job (生命周期管理)和 Continuation(恢复回调)。

对于 LAZY 模式,创建的是 LazyStandaloneCoroutine,初始状态为 EMPTY_NEW(非活跃),只有调用 start()/join() 时才会触发执行。


四、启动分发:CoroutineStart

coroutine.start(start, coroutine, block) 最终会调用 CoroutineStart 的操作符重载:

kotlin

复制代码
// kotlinx.coroutines.CoroutineStart
public operator fun <R, T> invoke(
    block: suspend R.() -> T,
    receiver: R,
    completion: Continuation<T>
): Unit = when (this) {
    DEFAULT     -> block.startCoroutineCancellable(receiver, completion)
    ATOMIC      -> block.startCoroutine(receiver, completion)
    UNDISPATCHED-> block.startCoroutineUndispatched(receiver, completion)
    LAZY        -> Unit // will start lazily
}

表格

| 模式 | 行为 |
|----------------|-------------------------------------|---|
| DEFAULT | 最常用 ,可响应取消,通过 Dispatcher 调度执行 |
| ATOMIC | 不可取消地启动,直到第一个挂起点才检查取消 |
| UNDISPATCHED | 直接在当前线程执行到第一个挂起点,不经过 Dispatcher |
| LAZY | 延迟启动,需手动触发 | |


五、核心:startCoroutineCancellable

这是 launch 源码中最关键的一行,也是协程真正"起飞"的地方:

kotlin

复制代码
// kotlinx.coroutines.intrinsics.Cancellable.kt
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit =
    runSafely(completion) {
        createCoroutineUnintercepted(completion)
            .intercepted()
            .resumeCancellableWith(Result.success(Unit))
    }

拆解为三步:

5.1 createCoroutineUnintercepted

编译器会为 suspend { ... } lambda 生成一个继承 SuspendLambda 的匿名类。此方法调用该类的 create() 工厂方法,new 出状态机实例。

kotlin

复制代码
// 编译器生成的伪代码
class MainActivity$onCreate$1 extends SuspendLambda {
    int label;  // 状态机标签
    
    Object invokeSuspend(Object $result) {
        switch (this.label) {
            case 0:
                this.label = 1;
                if (job1(this) == COROUTINE_SUSPENDED)
                    return COROUTINE_SUSPENDED;  // 挂起,等待恢复
            case 1:
                this.label = 2;
                if (job2(this) == COROUTINE_SUSPENDED)
                    return COROUTINE_SUSPENDED;
            case 2:
                return Unit.INSTANCE;
        }
    }
}

CPS 变换本质 :Kotlin 编译器将 suspend 函数编译为状态机 ,通过 label 控制执行流程,每个挂起点对应一个状态。

5.2 .intercepted()

kotlin

复制代码
// kotlin.coroutines.ContinuationInterceptor
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
    (this as? DispatchedContinuation<T>) ?: run {
        val dispatcher = context[ContinuationInterceptor] as ContinuationInterceptor
        dispatcher.interceptContinuation(this)
    }
  • CoroutineContext 中取出 ContinuationInterceptor(即 Dispatcher)。

  • DispatchedContinuation 包装原始 Continuation

  • 此后每次 resume 都会经过 Dispatcher 分发到对应线程

5.3 .resumeCancellableWith()

kotlin

复制代码
// kotlinx.coroutines.DispatchedContinuation
public fun resumeCancellableWith(result: Result<T>) {
    val dispatcher = delegate.context[ContinuationInterceptor]!!
    // 通过 Dispatcher 将恢复任务投递到目标线程
    dispatcher.dispatch(context, DispatchTask(this, result))
}

同时包装了取消检测 :在任务执行前检查 Job.isActive,若已取消则直接走取消流程,不执行业务代码。


六、Android 主线程调度:Dispatchers.Main

在 Android 平台,Dispatchers.Main 的实现是 HandlerContext

kotlin

复制代码
// kotlinx.coroutines.android.HandlerDispatcher.kt
internal class HandlerContext(
    private val handler: Handler,
    private val name: String? = null
) : HandlerDispatcher(), Delay {
    
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        // 通过 Handler.post 将协程任务投递到主线程 Looper
        handler.post(block)
    }
    
    override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
        // delay 的实现:Handler.postDelayed
        handler.postDelayed(
            Runnable { continuation.resumeUndispatched(Unit) },
            timeMillis
        )
    }
}

执行链路

plain

复制代码
resumeCancellableWith()
    → HandlerContext.dispatch()
        → handler.post(Runnable { continuation.resumeWith(result) })
            → 主线程 Looper 取出消息
                → BaseContinuationImpl.resumeWith()
                    → invokeSuspend() 继续执行状态机

Dispatchers.Main.immediate 会额外检查 Looper.myLooper() == Looper.getMainLooper(),若已在主线程则直接执行,避免一帧延迟。


七、挂起与恢复的完整链路

挂起(Suspend)

kotlin

复制代码
// 以 delay 为例
public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return
    return suspendCancellableCoroutine { cont: CancellableContinuation<Unit> ->
        // 注册一个定时器,时间到后调用 cont.resume()
        scheduleResumeAfterDelay(timeMillis, cont)
    }
}

suspendCancellableCoroutine 会:

  1. 将当前 Continuation 挂起。

  2. 返回 COROUTINE_SUSPENDED

  3. invokeSuspend() 收到 COROUTINE_SUSPENDED 后直接返回,当前线程被释放

恢复(Resume)

复制代码
定时器到期 / IO 完成 / 用户点击
    ↓
CancellableContinuation.resume(Unit)
    ↓
DispatchedContinuation.resumeWith(Result.success(Unit))
    ↓
Dispatcher.dispatch(context, task)  ← 决定恢复线程
    ↓
线程执行任务
    ↓
BaseContinuationImpl.resumeWith(result)
    ↓
invokeSuspend() 根据 label 跳转到对应状态继续执行

八、结构化并发:父子 Job 的绑定

StandaloneCoroutine 的父 Job 来自 CoroutineScope 的上下文。在 AbstractCoroutine 初始化时:

kotlin

复制代码
// kotlinx.coroutines.JobSupport
init {
    if (initParentJob) {
        // 将当前协程作为子 Job 注册到父 Job
        parentContext[Job]?.attachChild(this)
    }
}

这意味着:

  • Job cancel → 所有子协程收到取消信号。

  • 子协程异常 → 向上传播,父协程 cancel,兄弟协程连带取消(除非使用 SupervisorJob)。


九、完整调用链总结图

复制代码
用户代码: lifecycleScope.launch(Dispatchers.IO) { ... }
              │
              ▼
┌─────────────────────────────────────┐
│  launch(context, start=DEFAULT, block)│  ← Builders.common.kt
│  val newContext = newCoroutineContext │
│  → combined = scope.context + IO    │
│  → + Dispatchers.Default (若未指定) │
└─────────────────────────────────────┘
              │
              ▼
┌─────────────────────────────────────┐
│  StandaloneCoroutine(newContext)    │  ← 继承 AbstractCoroutine/JobSupport
│  initParentJob = true → 绑定父 Job  │
└─────────────────────────────────────┘
              │
              ▼
┌─────────────────────────────────────┐
│  coroutine.start(DEFAULT, self, block)│
│  → block.startCoroutineCancellable  │
└─────────────────────────────────────┘
              │
              ▼
┌─────────────────────────────────────┐
│  createCoroutineUnintercepted()     │  ← 编译器生成 SuspendLambda
│  .intercepted()                     │  ← IO Dispatcher 包装
│  .resumeCancellableWith(Unit)      │  ← 提交到 IO 线程池
└─────────────────────────────────────┘
              │
              ▼
        IO 线程执行 block
              │
        遇到 delay/withContext/suspend 函数
              │
        返回 COROUTINE_SUSPENDED
              │
        线程释放,回到线程池
              │
        时间到 / 回调触发
              │
        Dispatcher.dispatch → IO 线程
              │
        resumeWith() → invokeSuspend()
              │
        状态机 label++,继续执行
              │
        withContext(Main) → Handler.post → 主线程
              │
        更新 UI ...
              │
        执行完毕 → StandaloneCoroutine 进入 Completed 状态
              │
        通知父 Job → 子 Job 完成

十、关键源码文件索引

功能 文件路径
launch / async / runBlocking kotlinx.coroutines.Builders.common.kt
上下文合并 kotlinx.coroutines.CoroutineContext.common.kt
Job 状态机 kotlinx.coroutines.JobSupport.kt
可取消启动 kotlinx.coroutines.intrinsics.Cancellable.kt
Continuation.intercepted() kotlin.coroutines.jvm.internal.ContinuationImpl.kt
Android 主线程调度 kotlinx.coroutines.android.HandlerDispatcher.kt
Dispatchers.Default/IO kotlinx.coroutines.scheduling.DefaultScheduler.kt
相关推荐
Android-Flutter2 小时前
android kotlin 状态机 Continuation 详解
android·kotlin
Zha0Zhun3 小时前
Jetpack Compose 实现 iOS 风格 3D WheelPicker
android
深念Y4 小时前
RIO-UL00(EMUI 4.1 / Android 6.0.1 / arm64)开机自启动 sshd
android·linux·华为·安卓·chroot·sshd·emui
爱和冰阔落4 小时前
【MySQL 慢查询排查实战】列表接口逐渐变慢时,怎样从请求链路定位原因
android·数据库·mysql
hunterandroid20 小时前
Android WebView JSBridge 治理实战:从线上白屏崩溃到协议化通信
android·前端
一个用户名i21 小时前
【Compose 系列】第 1 篇:认识 Compose,为什么要学它
android·android jetpack
矩子01X21 小时前
保姆级智能座舱测试入门:从 Android Automotive OS 到 SOA 服务的 7 层验证
android·自动化测试·网络协议·汽车测试
极客猴子1 天前
iPhone实时转写软件推荐:会议录音功能真实体验
android·人工智能·飞书