上篇文章我们介绍了 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
恢复取消。我们一个一个来看。
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