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
相关推荐
ai2work27 分钟前
ch06 Jetpack Compose 入门:状态驱动 UI
kotlin
邪修king29 分钟前
Re:Linux 系统篇(二十九):动静态库Chapter2:动态库深度辨析 —— 核心本质、制作流程、双阶段查找模型与排错指南
android·java·linux·开发语言
Mr YiRan8 小时前
网络请求API监控与网络切换埋点
android·网络
美狐美颜sdk11 小时前
直播APP开发技术栈详解:视频美颜SDK、人脸识别与实时渲染
android·人工智能·音视频·美颜sdk·直播美颜sdk
奈斯先生Vector17 小时前
当模型版本不断变化,RelayRouter 能否帮助 AI 应用摆脱深度绑定
android·java·人工智能·开源·aigc
TimeFine17 小时前
让强模型做“总工”,让高性价比模型写代码
android
又见情义18 小时前
RK3568 Android 13 屏蔽 healthd 电池日志经验分享
android
古法安卓21 小时前
Android-Fork 机制详解
android·java·android studio
JMchen1 天前
第 12 篇|项目整合与打包发布 —— 从 Demo 到可安装 APK 的完整收官指南
kotlin·android studio
Android打工仔1 天前
不要在 Data 层随意把 Cold Flow 转换成 Hot Flow
android·架构·kotlin