这章主要想剖析下协程的结构,如果你已经在项目中用过协程,那么我想用过最多的方式有可能是如下俩种:
viewModelScope.launch{......}GlobalScope.launch{......}
至于 viewModelScope 和 GlobalScope 是什么,launch 方法是什么,为什么在 launch 中就可以执行了 suspend 方法了等等这些问题可能并没有那么深入的了解。
其实上边那些问题都是协程的基础,如果不了解的话,那所使用的‘协程’也只能是人云亦云,别人怎么写我就怎么写而已,很难做到一丁儿点改变,就更别说优化。
这一章的内容是了解概念,抛开系统的封装,让我们来看下原始的协程
要了解协程的话,首先我们要创建一个协程,像上边内两种创建协程的方法 viewModelScope.launch 、 GlobalScope.launch 都是系统封装后的,而现在要做的就是抛开封装,自己创建。
下面,这是两个创建协程的方法源码:
Continuation.kt
/*** Creates a coroutine without a receiver and with result type T. This function creates a new, fresh instance of* suspendable computation every time it is invoked.** To start executing the created coroutine, invoke resume(Unit) on the returned Continuation instance.* The completion continuation is invoked when the coroutine completes with a result or an exception.* Subsequent invocation of any resume function on the resulting continuation will produce an IllegalStateException.*/@SinceKotlin("1.3")@Suppress("UNCHECKED_CAST")public fun <T> (suspend () -> T).createCoroutine(completion: Continuation<T>): Continuation<Unit> =SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)/*** Creates a coroutine with receiver type [R] and result type [T].* This function creates a new, fresh instance of suspendable computation every time it is invoked.** To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.* The [completion] continuation is invoked when the coroutine completes with a result or an exception.* Subsequent invocation of any resume function on the resulting continuation will produce an [IllegalStateException].*/@SinceKotlin("1.3")@Suppress("UNCHECKED_CAST")public fun <R, T> (suspend R.() -> T).createCoroutine(receiver: R,completion: Continuation<T>): Continuation<Unit> =SafeContinuation(createCoroutineUnintercepted(receiver, completion).intercepted(), COROUTINE_SUSPENDED)
这两个创建协程的方法一个是带作用域一个不带作用域,像我们用的 viewModelScope.launch 实际上就是第二种带作用域的方式,下面两段代码是两个创建实例:
不带作用域的:
fun myCreateCoroutine() {val myFun = suspend {println("In SuspendFunction")delay(1000)100}val continuation = myFun.createCoroutine(object : Continuation<Int> {override val context: CoroutineContextget() = EmptyCoroutineContextoverride fun resumeWith(result: Result<Int>) {println("in resumeWith$result")}})continuation.resume(Unit)}
带作用域的协程创建:
// 创建启动一个有作用域的协程的函数fun <R, T> launchCoroutine(receiver: R, block: suspend R.() -> T) {val continuation = block.createCoroutine(receiver, object : Continuation<T> {override val context: CoroutineContextget() = EmptyCoroutineContextoverride fun resumeWith(result: Result<T>) {println("in resumeWith$result")}})continuation.resume(Unit)}// 创建一个作用域class ProducerScope<T> {suspend fun produce(value: T) {delay(100)println("in produce $value")}}// 测试代码fun createReceiverCoroutine() {launchCoroutine(ProducerScope<Int>()) {println("In Coroutine")produce(1024)delay(1000)produce(2048)}}
测试代码:
fun main() {startLoop()myCreateCoroutine()println("---------- code end")}fun myCreateCoroutine() {println("---------- in createCoroutine start")val myFun = suspend {println("In SuspendFunction")delay(1000)100}val continuation = myFun.createCoroutine(object : Continuation<Int> {override val context: CoroutineContextget() = EmptyCoroutineContextoverride fun resumeWith(result: Result<Int>) {println("in resumeWith$result")}})continuation.resume(Unit)println("---------- in createCoroutine end")}fun startLoop() {Thread {while (true) {Thread.sleep(100)}}.start()}//-------- 测试结果输出-------//---------- in createCoroutine start//In SuspendFunction//---------- in createCoroutine end//---------- code end//in resumeWithSuccess(100)
希望你看到这还没有走,上面库库库一段代码不免让人看的云里雾里的,但是总得有个切入点。不知道你们看创建协程的源代码的时候有什么疑问,反正我看的时候就很三个很疑惑的地方:
- 创建协程我到底创建了个啥,咋用?
suspend () -> T和suspend R.() -> T是个 Lambda,而createCoroutine函数为这两个 Lambda 的扩展函数,那么这两个 Lambda 到底是啥,它的内容是什么时候被执行的?- 这个
Continuation是什么,为什么参数里有它返回值还是它,这俩什么关系?
首先说创建协程创建了什么
单说 myCreateCoroutine 函数,它内部创建了一个协程运行载体,刚写协程的时候我们都知道 suspend 函数规定必须要在 suspend 修饰的函数内执行,那么问题出现了,第一个 suspend 是怎么出来的,这个就是载体。例如下面代码:
fun selfFun(){val suspendFunction = suspend {println("In SuspendFunction")delay(1000)println("In SuspendFunction End")}suspendFunction.startCoroutine(object : Continuation<Unit> {override val context: CoroutineContextget() = EmptyCoroutineContextoverride fun resumeWith(result: Result<Unit>) {println("Coroutine End: $result")}})}fun globalFun(){GlobalScope.launch {println("In SuspendFunction")delay(1000)println("In SuspendFunction End")}}
对比下上述两个函数,如果只看载体相关的话,两个作用是一样的,都是定义了一个 suspend 载体,并且在里面运行其他的 suspend 的函数(delay)。
所以对于创建协程,我的理解是创建了一个协程载体可运行的环境
上边通过非常粗暴的方式解释了创建协程是什么有什么用(粗暴的理解为 GlobalScope.launch 的内部实现),那么我们肯定是对这个结果不满意的,丢失了太多的细节以至于跟没说一样。这个不是学习的结果和目的,为了实现熟练掌握协程,不能在一开头就草草而过。
那么接下来我们一步一步解开创建协程的秘密,首先是 suspend 的关键词
Suspend 方法
在讲 suspend 函数之前,我们先补下 Kotlin 的知识,在 Kotlin 中Lambda 到底是什么,下面是我新建的一个 LambdaTest.kt 的文件,并写入如下代码:
fun main() {val noSuspendLambda = {println("in noSuspendLambda")}}
这段代码很简单,就是创建了一个 Lambda,那么它是如何符合 jvm 规范的呢,想破头脑不如反编译一下,如下图:
通过反编译以后的字节码,可以看到 noSuspendLambda 被编译器加工成了一个类,继承了 Lambda 并且实现了 Function0。
有了如上信息后,我猜你已经知道如何去看 suspend lambda 到底是什么了,废话不多说,修改下代码:
fun main() {val suspendLambda = suspend {println("in suspendLambda")}val suspendFun:(suspend () -> Unit) = ::suspendFun}suspend fun suspendFun(){println("in suspendFun")}
反编译后的截取
通过
附录
反编译 Kotlin 代码

