这章主要想剖析下协程的结构,如果你已经在项目中用过协程,那么我想用过最多的方式有可能是如下俩种:

  1. viewModelScope.launch{
  2. ......
  3. }
  4. GlobalScope.launch{
  5. ......
  6. }

至于 viewModelScope 和 GlobalScope 是什么,launch 方法是什么,为什么在 launch 中就可以执行了 suspend 方法了等等这些问题可能并没有那么深入的了解。

其实上边那些问题都是协程的基础,如果不了解的话,那所使用的‘协程’也只能是人云亦云,别人怎么写我就怎么写而已,很难做到一丁儿点改变,就更别说优化。

这一章的内容是了解概念,抛开系统的封装,让我们来看下原始的协程

要了解协程的话,首先我们要创建一个协程,像上边内两种创建协程的方法 viewModelScope.launchGlobalScope.launch 都是系统封装后的,而现在要做的就是抛开封装,自己创建。

下面,这是两个创建协程的方法源码:

Continuation.kt

  1. /**
  2. * Creates a coroutine without a receiver and with result type T. This function creates a new, fresh instance of
  3. * suspendable computation every time it is invoked.
  4. *
  5. * To start executing the created coroutine, invoke resume(Unit) on the returned Continuation instance.
  6. * The completion continuation is invoked when the coroutine completes with a result or an exception.
  7. * Subsequent invocation of any resume function on the resulting continuation will produce an IllegalStateException.
  8. */
  9. @SinceKotlin("1.3")
  10. @Suppress("UNCHECKED_CAST")
  11. public fun <T> (suspend () -> T).createCoroutine(
  12. completion: Continuation<T>
  13. ): Continuation<Unit> =
  14. SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
  15. /**
  16. * Creates a coroutine with receiver type [R] and result type [T].
  17. * This function creates a new, fresh instance of suspendable computation every time it is invoked.
  18. *
  19. * To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
  20. * The [completion] continuation is invoked when the coroutine completes with a result or an exception.
  21. * Subsequent invocation of any resume function on the resulting continuation will produce an [IllegalStateException].
  22. */
  23. @SinceKotlin("1.3")
  24. @Suppress("UNCHECKED_CAST")
  25. public fun <R, T> (suspend R.() -> T).createCoroutine(
  26. receiver: R,
  27. completion: Continuation<T>
  28. ): Continuation<Unit> =
  29. SafeContinuation(createCoroutineUnintercepted(receiver, completion).intercepted(), COROUTINE_SUSPENDED)

这两个创建协程的方法一个是带作用域一个不带作用域,像我们用的 viewModelScope.launch 实际上就是第二种带作用域的方式,下面两段代码是两个创建实例:

不带作用域的:

  1. fun myCreateCoroutine() {
  2. val myFun = suspend {
  3. println("In SuspendFunction")
  4. delay(1000)
  5. 100
  6. }
  7. val continuation = myFun.createCoroutine(object : Continuation<Int> {
  8. override val context: CoroutineContext
  9. get() = EmptyCoroutineContext
  10. override fun resumeWith(result: Result<Int>) {
  11. println("in resumeWith$result")
  12. }
  13. })
  14. continuation.resume(Unit)
  15. }

带作用域的协程创建:

  1. // 创建启动一个有作用域的协程的函数
  2. fun <R, T> launchCoroutine(receiver: R, block: suspend R.() -> T) {
  3. val continuation = block.createCoroutine(receiver, object : Continuation<T> {
  4. override val context: CoroutineContext
  5. get() = EmptyCoroutineContext
  6. override fun resumeWith(result: Result<T>) {
  7. println("in resumeWith$result")
  8. }
  9. })
  10. continuation.resume(Unit)
  11. }
  12. // 创建一个作用域
  13. class ProducerScope<T> {
  14. suspend fun produce(value: T) {
  15. delay(100)
  16. println("in produce $value")
  17. }
  18. }
  19. // 测试代码
  20. fun createReceiverCoroutine() {
  21. launchCoroutine(ProducerScope<Int>()) {
  22. println("In Coroutine")
  23. produce(1024)
  24. delay(1000)
  25. produce(2048)
  26. }
  27. }

测试代码:

  1. fun main() {
  2. startLoop()
  3. myCreateCoroutine()
  4. println("---------- code end")
  5. }
  6. fun myCreateCoroutine() {
  7. println("---------- in createCoroutine start")
  8. val myFun = suspend {
  9. println("In SuspendFunction")
  10. delay(1000)
  11. 100
  12. }
  13. val continuation = myFun.createCoroutine(object : Continuation<Int> {
  14. override val context: CoroutineContext
  15. get() = EmptyCoroutineContext
  16. override fun resumeWith(result: Result<Int>) {
  17. println("in resumeWith$result")
  18. }
  19. })
  20. continuation.resume(Unit)
  21. println("---------- in createCoroutine end")
  22. }
  23. fun startLoop() {
  24. Thread {
  25. while (true) {
  26. Thread.sleep(100)
  27. }
  28. }.start()
  29. }
  30. //-------- 测试结果输出-------
  31. //---------- in createCoroutine start
  32. //In SuspendFunction
  33. //---------- in createCoroutine end
  34. //---------- code end
  35. //in resumeWithSuccess(100)

希望你看到这还没有走,上面库库库一段代码不免让人看的云里雾里的,但是总得有个切入点。不知道你们看创建协程的源代码的时候有什么疑问,反正我看的时候就很三个很疑惑的地方:

  1. 创建协程我到底创建了个啥,咋用?
  2. suspend () -> Tsuspend R.() -> T 是个 Lambda,而 createCoroutine 函数为这两个 Lambda 的扩展函数,那么这两个 Lambda 到底是啥,它的内容是什么时候被执行的?
  3. 这个 Continuation 是什么,为什么参数里有它返回值还是它,这俩什么关系?

首先说创建协程创建了什么

单说 myCreateCoroutine 函数,它内部创建了一个协程运行载体,刚写协程的时候我们都知道 suspend 函数规定必须要在 suspend 修饰的函数内执行,那么问题出现了,第一个 suspend 是怎么出来的,这个就是载体。例如下面代码:

  1. fun selfFun(){
  2. val suspendFunction = suspend {
  3. println("In SuspendFunction")
  4. delay(1000)
  5. println("In SuspendFunction End")
  6. }
  7. suspendFunction.startCoroutine(object : Continuation<Unit> {
  8. override val context: CoroutineContext
  9. get() = EmptyCoroutineContext
  10. override fun resumeWith(result: Result<Unit>) {
  11. println("Coroutine End: $result")
  12. }
  13. })
  14. }
  15. fun globalFun(){
  16. GlobalScope.launch {
  17. println("In SuspendFunction")
  18. delay(1000)
  19. println("In SuspendFunction End")
  20. }
  21. }

对比下上述两个函数,如果只看载体相关的话,两个作用是一样的,都是定义了一个 suspend 载体,并且在里面运行其他的 suspend 的函数(delay)。
所以对于创建协程,我的理解是创建了一个协程载体可运行的环境

上边通过非常粗暴的方式解释了创建协程是什么有什么用(粗暴的理解为 GlobalScope.launch 的内部实现),那么我们肯定是对这个结果不满意的,丢失了太多的细节以至于跟没说一样。这个不是学习的结果和目的,为了实现熟练掌握协程,不能在一开头就草草而过。

那么接下来我们一步一步解开创建协程的秘密,首先是 suspend 的关键词

Suspend 方法


在讲 suspend 函数之前,我们先补下 Kotlin 的知识,在 Kotlin 中Lambda 到底是什么,下面是我新建的一个 LambdaTest.kt 的文件,并写入如下代码:

  1. fun main() {
  2. val noSuspendLambda = {
  3. println("in noSuspendLambda")
  4. }
  5. }

这段代码很简单,就是创建了一个 Lambda,那么它是如何符合 jvm 规范的呢,想破头脑不如反编译一下,如下图:
image.png
通过反编译以后的字节码,可以看到 noSuspendLambda 被编译器加工成了一个类,继承了 Lambda 并且实现了 Function0。

有了如上信息后,我猜你已经知道如何去看 suspend lambda 到底是什么了,废话不多说,修改下代码:

  1. fun main() {
  2. val suspendLambda = suspend {
  3. println("in suspendLambda")
  4. }
  5. val suspendFun:(suspend () -> Unit) = ::suspendFun
  6. }
  7. suspend fun suspendFun(){
  8. println("in suspendFun")
  9. }

反编译后的截取
image.png
通过

附录


反编译 Kotlin 代码

image.png