本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布[2023-03-02]

前言: kotlin协程源码十分庞大, 本篇只能吧我理解的源码聊一聊,不会特别深入研究,只会浅浅的看看表层. 本来方案协程系列是10篇左右,后续是flow暖流冷流之类的, 冷流操作符之类的应该不会在写了, flow当作Rxjava来用就能够,后续可能还会写一篇关于暖流的文章. 也可能没有:) 主要是不好写,文字写出来还是比较生硬….

如果没有看过前几篇,主张先看看前几篇, 本篇遇到前几篇的常识不会重复说!

launch 浅析

源码阅览从最简略的一个launch开始!

android kotlin 协程(六) 源码浅析

在launch的时分, 会履行 CoroutineScope.newCoroutineContext函数 这儿会传入一个EmptyCoroutineContext

CoroutineScope.newCoroutineContext 会走 foldCopies , 这个函数是用来合并2个协程的

先来看看 foldCopies的参数

  • coroutineContext // 能够看出,此刻coroutineContext为JobImpl 咱们稍后来看看它是在什么时分赋值的
  • context // 默许什么都没有传,是 EmptyCoroutineContext
  • true // isNewCoroutine 是否创立新的Coroutine

咱们在创立CoroutineScope的时分,会对coroutineContext赋值

android kotlin 协程(六) 源码浅析

咱们在创立协程作用域的时分, 会初始化一个Job, Job的默许实现为JobImpl

好了,在回到 CoroutineScope.newCoroutineContext办法

android kotlin 协程(六) 源码浅析

履行完foldCopies后,

咱们知道此刻回来成果combined = jobImpl,

最后当回来的时分 if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)

终究给他增加一个默许调度器 [Dispatchers.Default]

tips: ContinuationInterceptor 是协程阻拦器, 下面会说,不要急

再次回到主线流程

android kotlin 协程(六) 源码浅析

此刻newContext = [JobImpl , Dispatchers.Default]

如果说,有子协程的情况下,newCoroutineContext 这个办法就会运用最新的coroutineContext, 例如这样:

android kotlin 协程(六) 源码浅析

这都是 foldCopies 的劳绩, foldCopies的细节就不看了,没意义 [比较烧脑,不想看]

再接着往下走:

android kotlin 协程(六) 源码浅析

在聊协程发动形式的时分说过,coroutineStart一共有4中形式

  • DEFAULT
  • LAZY
  • UNDISPATCHED
  • ATOMIC // 实验中

此刻会判断是否是懒加载形式, 很明显不是懒加载,所以会走 StandaloneCoroutine

StandaloneCoroutine 会经过 handleJobException 捕获一些异常.

比如说在JVM中运用Main线程的时分,会提示

Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used

在这儿就能捕获到

android kotlin 协程(六) 源码浅析

在继续往下看:

android kotlin 协程(六) 源码浅析

接下来便是敞开一个协程

这儿经过kotlin 的特性重载操作符,直接去它类中找到对应的办法即可

由于此刻 协程发动形式是DEFAULT,那么直接看:

block.startCoroutineCancellable(receiver, completion)

即可

在接着往下看

createCoroutineUnintercepted(receiver, completion) 函数 源码方位

这个函数需求去kotiln官网看,太深了,我没看,我找到了 提供给需求的人!

这儿看看阻拦的办法

android kotlin 协程(六) 源码浅析

能够看出,此刻就会分发 continuation

先来看看不写 ContinuationInterceptor 是什么作用

android kotlin 协程(六) 源码浅析

这段代码写过无数次, 由于协程默许发动形式需求调度,所以协程体内的代码还没来得及履行,main函数就现已结束了

那么在来看看增加协程阻拦器后有什么改变

android kotlin 协程(六) 源码浅析

能够看出, 协程会立即履行,我猜测是阻拦的过程中会立即触发康复,所以才会有这样的作用, 可惜的是我没找到康复的代码

跟随源码手动创立协程

刚才看源码的时分看到了这段代码

android kotlin 协程(六) 源码浅析

敞开一个协程的时分, 经过函数扩展 扩展了 startCoroutineCancellable

那么咱们是否也能够测验的写一写

这儿有一个留意点:

咱们并不能调用 startCoroutineCancellable由于它是内部办法,咱们无法直接调用

android kotlin 协程(六) 源码浅析

只能拿 createCoroutine 来替代

来看看咱们的代码:

android kotlin 协程(六) 源码浅析

这儿我把范型都替换成了详细的值,否则的话看的更迷糊

data class TestBean(val message: String)
fun main() {
    fun test(receiver: TestBean, block: suspend TestBean.() -> String) {
            block.createCoroutine(receiver, object : Continuation<String> { // 创立协程
                override val context: CoroutineContext
                    get() = EmptyCoroutineContext
                override fun resumeWith(result: Result<String>) {// 当协程康复的时分履行
                    println("resume:$result")
                }
            })
    }
    val bean = TestBean("测试数据")
    test(bean) {
        println("我是test内部办法${this}")
        "我是回来数据"
    }
}

代码都写完了,可是这儿并没有任何成果!

没有成果是正常现象,由于咱们仅仅创立了一个协程,默许是挂起状况, 只要康复的时分才会履行代码

例如这样:

android kotlin 协程(六) 源码浅析

如果你觉得这样很麻烦,也能够直接

将 createCoroutine 改为 startCoroutine

  • createCoroutine // 创立协程 [默许挂起]
  • startCoroutine // 直接敞开一个协程

android kotlin 协程(六) 源码浅析

那么咱们自定义CoroutineContxt是否还起作用呢?

android kotlin 协程(六) 源码浅析

这儿的时分,咱们经过Job#cancel() 能够发现,并没有取消协程,康复代码还是继续履行了

这是由于当咱们运用 launch{} 敞开一个协程的时分, 系统帮咱们保护了job的各种状况

这儿很简略,咱们自己保护job即可

android kotlin 协程(六) 源码浅析

系统代码指定是没有这么简略,不过模拟一下就能够了,没必要太纠结

那如果是异常如何捕获呢?

android kotlin 协程(六) 源码浅析

需求留意的是,经过 CoroutineExceptionHandler捕获异常,这儿监听的是 resumeWith办法

android kotlin 协程(六) 源码浅析

只需求再次向上throw一下即可

简略的

刚才咱们介绍的是有receiver的 startCoroutine,还有一个没有receiver的

android kotlin 协程(六) 源码浅析

直接来看代码:

android kotlin 协程(六) 源码浅析

这段代码很简答, 仅仅创立一个一般的协程,并康复, 当然也能够经过CoroutineContext来操控协程,上面现已提到了,这儿就不重复了

有创立协程手动康复,就有直接创立并康复

android kotlin 协程(六) 源码浅析

有receiver和没有receiver的差异也很明显, 那便是在suspend办法体中是否有receiver.

总结

本篇咱们阅览了 CoroutineScope#launch{}

并学习了手动管理协程的4个办法:

  • (suspend R.() -> T).startCoroutine // 有receiver直接康复协程

  • (suspend R.() -> T).createCoroutine // 有receiver创立协程,需求手动康复

  • (suspend () -> T).startCoroutine // 没有receiver直接康复协程

  • (suspend () -> T).createCoroutine // 没有receiver创立协程,需求手动康复

这四胞胎说实话,是真没啥用,但便是得了解一下.. 看的头疼:)

完整代码

原创不易,您的点赞便是对我最大的支持!