Kotlin协程之旅:探索异步编程的魅力——原理篇

一、概述

上篇文章我们介绍了 kotlin 协程的具体使用方法,并且理解了什么是协程的创建挂起。这篇文章我们将从源码探索一下协程创建和挂起的底层逻辑

学习记录型的博客,有任何错误请多多包涵,欢迎各位大佬在评论区发表自己的理解和建议。

二、寻找创建函数

众所周知,协程启动的最常见的函数就说launch接下来我们就从 launch开始说起。先看一下launch的源码。

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext, // 协程的上下文,默认是空上下文
    start: CoroutineStart = CoroutineStart.DEFAULT,   // 协程启动方式,默认是默认方式
    block: suspend CoroutineScope.() -> Unit         // 协程执行的挂起函数体
): Job {
    // 创建一个新的协程上下文,基于传入的 context
    val newContext = newCoroutineContext(context)  
    // 如果启动方式是惰性启动(Lazy),创建一个 LazyStandaloneCoroutine,否则创建一个 StandaloneCoroutine
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block)   // 惰性协程:只有被启动时才开始执行
    else
        StandaloneCoroutine(newContext, active = true) // 普通协程:立即启动并处于活动状态
    
    // 启动协程,传入启动方式、协程对象以及执行的 block 函数
    coroutine.start(start, coroutine, block)
    // 返回创建的协程对象也就是 Job,用于表示协程的生命周期
    return coroutine
}

继续进入

public fun  start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
    start(block, receiver, this)
}

进入到这里,可能就会卡住了。我们仔细观察肯定会发现,start不是一个变量名吗?为什么能在后面加括号。这里就要说到

这是因为 start 的调用涉及到运算符重载,实际上会调到 CoroutineStart.invoke() 方法。

举个例子大家就明白了:

class Example {
    operator fun invoke() {
        println("Called Example object as a function")
    }
}

fun main() {
    val example = Example()
    example()  // 这实际上是调用 example.invoke()
}

在 Kotlin 中,operator 关键字用于标记特定的函数。运算符重载允许开发者为内置运算符(例如 +, -, *, [], =, 等)定义自定义的行为。这让你可以像使用内置类型一样使用自定义类,并且实现直观、简洁的运算符操作。

既然找到了具体函数的位置,那我们就继续。

@InternalCoroutinesApi
public operator fun  invoke(block: suspend () -> T, completion: Continuation): Unit {
    when (this) {
        DEFAULT -> block.startCoroutineCancellable(completion) // 默认启动方式,支持取消
        
        ATOMIC -> block.startCoroutine(completion) // 原子启动方式,无法被取消

        UNDISPATCHED -> block.startCoroutineUndispatched(completion) // 不使用调度器,直接执行

        LAZY -> Unit // 懒启动,不做任何操作,直到显式启动
    }
}
@InternalCoroutinesApi
public fun  (suspend () -> T).startCoroutineCancellable(completion: Continuation): Unit = runSafely(completion) {
    createCoroutineUnintercepted(completion)
        .intercepted()
        .resumeCancellableWith(Result.success(Unit))
}

到这里出现了一连串的三个方法调用,我们遵循看函数先看名字的原则,简单翻译一下:create创建Unintercepted未截获;intercepted截获;resumeCancellable恢复取消。我们一个一个来看。

1.createCoroutineUnintercepted

ctrl + 左键 进入,人傻了这是哪门子的函数啊。

@kotlin.SinceKotlin public fun  (suspend () -> T).createCoroutineUnintercepted(completion: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation {
    /* compiled code */ 
}

compiled code:编译代码。完蛋,怎么代码还有编译的时候自己生成的?不知道,那就学习!

这一行注释表示函数的实现是由编译器自动生成的代码,通常你在源码中看不到具体的实现。

经过我一番寻找,查资料结果发现了:上面这段代码所在文件的结尾是 .class 。真正的代码存放在末尾带有 Jvm 的文件中。

IntrinsicsJvm.kt下文件 BaseContinuationImpl 抽象类中。

@SinceKotlin("1.3")
public actual fun  (suspend R.() -> T).createCoroutineUnintercepted(
    receiver: R,  // 协程的接收者对象,传递给挂起函数
    completion: Continuation  // 协程完成后的回调接口,处理协程结果或异常
        ): Continuation {  // 返回一个 Continuation 对象,表示协程的控制
    // 使用 probeCoroutineCreated 函数处理传入的 completion,通常用于监控协程创建过程
    val probeCompletion = probeCoroutineCreated(completion)

    // 判断当前的挂起函数是否是 BaseContinuationImpl 类型
    // BaseContinuationImpl 是协程实现类,表示协程的基本实现
    return if (this is BaseContinuationImpl)
    // 如果是 BaseContinuationImpl,调用 create 方法创建协程
    // create 方法会使用 receiver 和 probeCompletion

你可能感兴趣的:(android,kotlin)