引子:从协程的启动聊起
回顾在 Java/Android中如何启动一个线程
arduino
val thread = Thread {
println("hello thread!")
}
thread.start()
创建一个线程对象,然后 start,很简单!
启动一个协程(准确的讲是使用 Global coroutine scope 启动)
scss
GlobalScope.launch {
println("i am in the coroutine")
}
乍看并没有发现诸如创建协程(coroutine)对象,启动之类的方法,代以口语化的 launch 调用
它一定在后面帮我们做了很多事情(封装)
kotlin
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CorotuineStart = CoutineStart.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)
return coroutine
}
emm,一堆新名词(概念、抽象)!
java
CoroutineScope
CoroutineStart
CoroutineContext
Coroutine (StandaloneCoroutine, LazyStandaloneCoroutine)
Job
好在命名很规范,即使对协程一窍不通,从名字也能猜出一二,先不着急展开
总结:launch 这个协程 builder 帮我们干(封装)了很多事,避免了写一堆样板代码,包括:
- 创建默认的协程上下文(CoroutineContext)
- 创建协程对象(Coroutine)
- 启动协程(start)
启动协程的代码和(创建)和启动线程的代码很像
lua
/*
start: 启动参数,或者说是启动模式
coroutine: 思考为什么要把 coroutine 自个儿传进去?
block: 这个好理解,协程要执行的代码片段
*/
coroutine.start(start, coroutine, block)
接着 STEP INTO,start 的具体实现在 AbstractCoroutine.kt 中
kotlin
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
start(block, receiver, this)
}
emmm,是眼花了吗?好像递归了?start 方法里头的 start 调用的是 CoroutineStart 的invoke operator(函数调用操作符重载)...
kotlin
// CoroutineStart.kt
public operator fun<R, T> invoke(
block: suspend R.() -> T,
receiver: R, completion: Continuation<T>,
completion: Continuation<T>
): Unit = {
when(this) {
DEFAULT -> block.startCoroutineCancellable(receiver, completion)
......
}
}
又是扩展函数!,kotlin coroutnie 框架里用到了大量的扩展函数,习惯就好...
kotlin
// Cancelable.kt
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
receiver: R,
completion: Continuation<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
) =
runSafely(completion) {
createCoroutineUnintercepted(receiver, completion)
.intercepted()
.resumeCancellableWith(Result.success(Unit), onCancellation)
}
}
runSafely,这个也是一目了然,为了安全的执行尾部的 lamda 表达式经验丰富的你应该能猜到startCoroutineCancellable 和 createCoroutineUnintercepted 也是扩展方法
仔细观察链式调用三连:create/intercepted/resume:
- 创建 create
- 拦截 intercepte
- 恢复 resume
创建好理解,但是拦截和恢复是什么鬼?随着对源代码理解的深入,慢慢细品
创建:createCoroutineUnintercepted 并不是创建 coroutine,而是创建一个叫 Continuation 的东西,你可以把 Continuation 想象成对状态机的封装,通过 Continuation (resumeWith)执行 suspend lambda(block)
恢复:这是真正执行协程(suspend lambda)的入口,如果把 suspend lambda 想象成一个机器 拦截:包装器模式,它拦截了对resume的调用,使得代码可以被调度,说的直白点,可以在线程池中执行
我们来看看创建方法
kotlin
public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
Completion: Continuation<T>
): Continuation<Unit> {
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
create(probeCompletion)
else
createCoroutineFromSuspendFunction(probeCompletion) {
...
}
}
大多数情况下 this is BaseContinuationImpl 是成立的,通过 create 方法创建一个 Continuation 第一次看到这段代码是有疑问的, 这个 create 可以推测出是 BaseContinuationImpl 的一个方法,查看相关源码也能证实这一点
但是,为什么 suspend lambda is 啊 BaseContinuationImpl? 为了寻求答案,可以反编译(开头那个)简单的例子
kotlin
fun main() {
GlobalScope.launch {
println("hello, coroutine")
}
}
下面这一长串有点冲稿费的嫌疑,可以重点关注注释的那几行
bash
// access flags 0x19
public final static main()V
L0
LINENUMBER 7 L0
GETSTATIC kotlinx/coroutines/GlobalScope.INSTANCE : Lkotlinx/coroutines/GlobalScope;
CHECKCAST kotlinx/coroutines/CoroutineScope
ACONST_NULL
ACONST_NULL
// 创建 一个内部类的对象,它就是 launch 的那个 suspend lambda
NEW com/dreamworker/kotlin/coroutine/RunBlockingKt$main$1
DUP
ACONST_NULL
INVOKESPECIAL com/dreamworker/kotlin/coroutine/RunBlockingKt$main$1.<init> (Lkotlin/coroutines/Continuation;)V
CHECKCAST kotlin/jvm/functions/Function2
ICONST_3
ACONST_NULL
INVOKESTATIC kotlinx/coroutines/BuildersKt.launch$default (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job;
POP
L1
LINENUMBER 10 L1
NOP
RETURN
MAXSTACK = 6
MAXLOCALS = 0
// access flags 0x1009
public static synthetic main([Ljava/lang/String;)V
L0
INVOKESTATIC com/dreamworker/kotlin/coroutine/RunBlockingKt.main ()V
NOP
RETURN
L1
LOCALVARIABLE args [Ljava/lang/String; L0 L1 0
MAXSTACK = 0
MAXLOCALS = 1
}
RunBlockingKt <math xmlns="http://www.w3.org/1998/Math/MathML"> m a i n main </math>main1 集成自 SuspendLambda, 而 SuspendLambda is a BaseContinuationImpl
scala
final class com/dreamworker/kotlin/coroutine/RunBlockingKt$main$1 extends kotlin/coroutines/jvm/internal/SuspendLambda implements kotlin/jvm/functions/Function2 {...}
到此做个总结,重要的事情说三遍: suspend lambda is a Continuation suspend lambda is a Continuation suspend lambda is a Continuation
回到启动协程的地方
kotlin
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
receiver: R,
completion: Continuation<T>,
onCancellation: ((cause: Throwable) -> Unit)? = null
) =
runSafely(completion) {
createCoroutineUnintercepted(receiver, completion)
.intercepted()
.resumeCancellableWith(Result.success(Unit), onCancellation)
}
我们浏览完 create 流程,这里先忽略 intercepted,因为它涉及到协程上下文,协程拦截(代理),协程派发等概念,resumeCancellableWith 方法开始执行协程!它的实现在基类 BaseContinuationImpl 中
kotlin
internal abstract class BaseContinuationImpl(
public val completion: Continuation<Any?>?
): Continuation<Any?>, CoroutineStackFrame, Serializable {
public final override resumeWith(result: Result<Any?>) {
while (true) {
...
invokeSuspend()
...
}
}
}
invokeSuspend 顾名思义,调用 suspend 方法,是个需要子类自己实现的抽象方法,
反编译 com/dreamworker/kotlin/coroutine/RunBlockingKt <math xmlns="http://www.w3.org/1998/Math/MathML"> m a i n main </math>main1 的 invokeSuspend 方法:
typescript
public final Object invokeSuspend(Object $result) {
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0:
ResultKt.throwOnFailure($result);
String var2 = "hello, coroutine";
System.out.println(var2);
return Unit.INSTANCE;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
}
终于看到了我们自个儿的那行 hello, coroutine!
待续!