协程挂起原理
先来看一个例子
结果就是先打印test3 ,然后5s过后依次打印 true-> test2 -> test1->test ,上面这个代码在kotlin看是看不出什么猫腻,因为kotlin 编译器会帮我们干很多事,我们转成java代码来看。
首先看test方法
在MainActivity1中我们看到调用test传了个this
所以r5就是MainActivity1 , MainActivity1继承自SuspendLambda ,用来启动协程,关于怎么启动协程这里就不展开了。
首先第一步会判断r5是不是com.example.test.MainActivity1 的实例,如果不是则执行L17 。com.example.test.MainActivity1是什么我们稍后再看,L17 中直接new com.example.test.MainActivity1将MainActivity1传入构造器中, 并将r0 指向它。然后接着执行L6 ,L6中就是协程状态机的代码了,刚开始 lable =0 ,会执行L14 此时会将lable =1 ,然后调用了test1 ,并把r5传入,并且r5 =r0 ,所以r5=com.example.test.MainActivity1 。
test1 ,test2 都类似我们就不看了,来看test3。
test3中开始部分还是和test类似,如果参数r11 不是com.example.test.MainActivity1则去执行L20 new com.example.test.MainActivity1然后将r0 指向它,接着执行协程状态机中的代码。
这里的状态机的代码就很有意思了,因为suspendCancellableCoroutine是内联方法,所以
suspendCancellableCoroutine中的代码经编译器处理都会在test3中。所以可以看到uCont 其实就等于 r11 = com.example.test.MainActivity1。协程状态机会先执行第一个状态0 ,并将状态 =1 ,状态0中会构建CancellableContinuationImpl 并将r11 传入其中,然后调用了Handler的post方法,最后调用CancellableContinuationImpl#getResult , getResult这里会先返回COROUTINE_SUSPENDED,接着执行L17 ,return COROUTINE_SUSPENDED 。此时test test1 test2 test3 都收到了return 的 COROUTINE_SUSPENDED , 所以都被挂起了。
总结一下: 如果每个suspend方法中会调用到其他suspend方法 ,编译器会给方法会生成类似下图的的类
这些类都是继承自ContinuationImpl , 被称为续体
以test3为例,当被恢复的时候最终会调用到invokeSuspend方法。记住此时 lable = lable | Integer.MIN_VALUE 然后再次执行test3方法 ,并且此时参数传入this 。
test3方法中 , lable&Integer.MIN_VALUE , 这个时候 1 | Integer.MIN_VALUE & Integer.MIN_VALUE 此时不等于0 ,所以不会走到L20 , 接着往下执行 1 | Integer.MIN_VALUE - Integer.MIN_VALUE = 1 , 继续执行L6 协程状态机中的此时Lable =1 所以走到L11 , L11执行完 接着执行L12 , L12就是 调用println(true),调用完之后 return Unit.INSTANCE 这个方法就结束了。
总结一下: 每个协程方法中如果调用了其他suspend 方法, 编译器就会给协程方法生成一个继承自ContinuationImpl的类,用来管理协程方法执行的状态 ,类名就是方法名后面拼接test1onCreateonCreatetest1test1$1中传入的父协程才是顶级协程,这么就不分析了,之前文章有写过。
粗略画了一张图。
协程恢复原理
记得我们调用 it.resume("") 就可以恢复协程执行了 , it没什么神秘的就是CancellableContinuationImpl ,我们跟进看看resume ,
来张官方的图,
tryResume() 方法会把状态改为RESUMED , 之前getResult的时候调用了trySuspend把状态改为SUSPENDED 。
调用resume最终会调用到DispatchedTask.resume ,delegate就是MainActivity1
internal open class CancellableContinuationImpl<in T>(
final override val delegate: Continuation<T>,
resumeMode: Int
)
internal fun <T> DispatchedTask<T>.resume(delegate: Continuation<T>, undispatched: Boolean) {
// This resume is never cancellable. The result is always delivered to delegate continuation.
val state = takeState()
val exception = getExceptionalResult(state)
val result = if (exception != null) Result.failure(exception) else Result.success(getSuccessfulResult<T>(state))
when {
undispatched -> (delegate as DispatchedContinuation).resumeUndispatchedWith(result)
//delegate = MainActivity$test3$1
else -> delegate.resumeWith(result)
}
}
所以会调用到MainActivity1的resumeWith , resumeWith之前分析过了,再贴一下,
MainActivity1#resumeWith
public final override fun resumeWith(result: Result<Any?>) {
var current = this
var param = result
while (true) {
probeCoroutineResumed(current)
with(current) {
//此时completion = MainActivity$test2$1
val completion = completion!!
val outcome: Result<Any?> =
try {
//执行MainActivity$test3$1 的invokeSuspend ,调用test3方法
val outcome = invokeSuspend(param)
//此时返回Unit.INSTANCE所以不会return
if (outcome === COROUTINE_SUSPENDED) return
Result.success(outcome)
} catch (exception: Throwable) {
Result.failure(exception)
}
releaseIntercepted() // this state machine instance is terminating
if (completion is BaseContinuationImpl) {
//把current = MainActivity$test2$1
//执行MainActivity$test2$1
//param = Unit.INSTANCE
current = completion
param = outcome
} else {
//是顶级协程。执行完这个协程就退出了
completion.resumeWith(outcome)
return
}
}
}
}
所以这个方法会依次执行MainActivity1 -> MainActivity1 -> MainActivity1 -> MainActivity1 -> AbstractCoroutine -> 退出协程
手写简单实现
以下是基于上述分析,手写了一个简单的实现,加速大家对挂起和恢复的理解。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
test(object : KContinuation<Any> {
override fun resumeWith(result: Any) {
println("main 顶级协程结束")
}
})
}
fun test(continuation: KContinuation<Any>): Any {
var curContinuation: KContinuation<Any> = continuation
if (curContinuation !is test_0) {
curContinuation = test_0(this, continuation)
}
when (curContinuation.lable) {
0 -> {
curContinuation.lable = 1
println("main test")
val res = test1(curContinuation)
if (res == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
}
1 -> {
}
else -> {
}
}
println("main test 恢复")
return Unit
}
fun test1(continuation: KContinuation<Any>): Any {
var curContinuation: KContinuation<Any> = continuation
if (curContinuation !is test_1) {
curContinuation = test_1(this, continuation)
}
when (curContinuation.lable) {
0 -> {
curContinuation.lable = 1
println("main test1")
Handler().postDelayed({
curContinuation.resumeWith("")
}, 5000)
return COROUTINE_SUSPENDED
}
1 -> {
}
else -> {
}
}
println("main test1 恢复")
return Unit
}
}
class test_0(val mainActivity: MainActivity, con: KContinuation<Any>) : KBaseContinuationImpl(con) {
var lable = 0
override fun invokeSuspend(result: Any): Any {
return mainActivity.test(this)
}
}
class test_1(val mainActivity: MainActivity, con: KContinuation<Any>) : KBaseContinuationImpl(con) {
var lable = 0
override fun invokeSuspend(result: Any): Any {
return mainActivity.test1(this)
}
}
interface KContinuation<T> {
public fun resumeWith(result: T)
}
abstract class KBaseContinuationImpl(public val completion: KContinuation<Any>?) :
KContinuation<Any> {
override fun resumeWith(result: Any) {
var current = this
var param = result
while (true) {
with(current) {
val completion =
completion!! // fail fast when trying to resume continuation without completion
val outcome: Any = try {
val outcome = invokeSuspend(param)
if (outcome === COROUTINE_SUSPENDED) return
Result.success(outcome)
} catch (exception: Throwable) {
Result.failure(exception)
}
if (completion is KBaseContinuationImpl) {
// unrolling recursion via loop
current = completion
param = outcome
} else {
completion.resumeWith(outcome)
return
}
}
}
}
protected abstract fun invokeSuspend(result: Any): Any
}