服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

Kotlin协程源码简单聊一聊,浅浅看看表层

日期: 来源:郭霖收集编辑:史大拿



/   今日科技快讯   /


近日,经历了一次停办,两次规模严重缩水之后,2023年世界移动通信大会(MWC)终于再现往日盛况:有来自200多个国家和地区的2000多家厂商参加,并在此次展会上发布了最新的产品与科技,仅中国就有100余家厂商参展。


每年的MWC都被称作“全球移动通信技术发展的风向标”,今年大会以“时不我待——明日科技,将至已至”为主题,具体又围绕着5G新动能、数字万物、开放网络、超越现实+ 、金融科技等五大热点主题展开。


/   作者简介   /


本篇文章转自史大拿的博客,文章主要分享了对kotlin协程源码的分析,相信会对大家有所帮助!


原文地址:

https://juejin.cn/post/7204752219915288636


/   前言   /


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


/   launch 浅析   /


源码阅读从最简单的一个launch开始!



在launch的时候,会执行CoroutineScope.newCoroutineContext函数。这里会传入一个EmptyCoroutineContext。CoroutineScope.newCoroutineContext会走foldCopies , 这个函数是用来合并2个协程的。


先来看看 foldCopies的参数:


  • coroutineContext // 可以看出,此时coroutineContext为JobImpl我们稍后来看看它是在什么时候赋值的
  • context // 默认什么都没有传,是EmptyCoroutineContext
  • true // isNewCoroutine是否创建新的Coroutine

我们在创建CoroutineScope的时候,会对coroutineContext赋值。


我们在创建协程作用域的时候,会初始化一个Job,Job的默认实现为JobImpl。好了,再回到CoroutineScope.newCoroutineContext方法。


执行完foldCopies后,我们知道此时返回结果combined = jobImpl,最后当返回的时候if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)最终给他添加一个默认调度器Dispatchers.Default。

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

再次回到主线流程。


此时newContext = [JobImpl,Dispatchers.Default],如果说有子协程的情况下,newCoroutineContext这个方法就会使用最新的coroutineContext。例如这样:


这都是foldCopies的功劳,foldCopies的细节就不看了,没意义。比较烧脑,不想看。再接着往下走:


在聊协程启动模式的时候说过,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,在这里就能捕获到。


继续往下看:


接下来就是开启一个协程。

这里通过kotlin的特性重载操作符,直接去它类中找到对应的方法即可。因为此时协程启动模式是DEFAULT,那么直接看block.startCoroutineCancellable(receiver, completion)即可。

接着往下看createCoroutineUnintercepted(receiver, completion)函数,这个函数需要去kotiln官网看,太深了,我没看,我找到了提供给需要的人!源码地址:
https://github.com/JetBrains/kotlin/blob/v1.8.20-Beta/libraries/stdlib/jvm/src/kotlin/coroutines/intrinsics/IntrinsicsJvm.kt

这里看看拦截的方法:


可以看出,此时就会分发continuation。先来看看不写ContinuationInterceptor是什么效果。


这段代码写过无数次,因为协程默认启动模式需要调度,所以协程体内的代码还没来得及执行,main函数就已经结束了。

那么在来看看添加协程拦截器后有什么变化。


可以看出,协程会立即执行,我猜测是拦截的过程中会立即触发恢复,所以才会有这样的效果,可惜的是我没找到恢复的代码。

/   跟随源码手动创建协程   /

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


开启一个协程的时候,通过函数扩展。扩展了startCoroutineCancellable。那么我们是否也可以尝试的写一写。

这里有一个注意点:我们并不能调用startCoroutineCancellable因为它是内部方法,我们无法直接调用。


只能拿createCoroutine来代替,来看看我们的代码:


这里我把范型都替换成了具体的值,否则的话看的更迷糊。

 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}")
        "我是返回数据"
    }
}

代码都写完了,但是这里并没有任何结果!

没有结果是正常现象,因为我们只是创建了一个协程,默认是挂起状态,只有恢复的时候才会执行代码。例如这样:


如果你觉得这样很麻烦,也可以直接将createCoroutine改为startCoroutine。

  • createCoroutine // 创建协程 [默认挂起]
  • startCoroutine // 直接开启一个协程



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


这里的时候,我们通过Job#cancel()可以发现,并没有取消协程,恢复代码还是继续执行了。

这是因为当我们使用launch{}开启一个协程的时候,系统帮我们维护了job的各种状态。这里很简单,我们自己维护job即可。


系统代码指定是没有这么简单,不过模拟一下就可以了,没必要太纠结。那如果是异常如何捕获呢?


需要注意的是,通过CoroutineExceptionHandler捕获异常,这里监听的是 resumeWith方法。


只需要再次向上throw一下即可。

/   简单的   /

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


直接来看代码:


这段代码很简单,只是创建一个普通的协程并恢复,当然也可以通过CoroutineContext来控制协程,上面已经提到了,这里就不重复了。有创建协程手动恢复,就有直接创建并恢复。


有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创建协程,需要手动恢复

这四胞胎说实话,是真没啥用,但就是得了解一下...

完整代码地址:
https://gitee.com/lanyangyangzzz/coroutine-project/blob/main/app/src/main/java/com/szj/coroutine/project/jvm/blog6/SafeContinuationTest.kt

推荐阅读:
我的新书,《第一行代码 第3版》已出版!
Kotlin协程开发的基础入门知识
LeakCanary是怎么检测到内存泄露的,看完这篇你就懂了

欢迎关注我的公众号
学习技术或投稿


长按上图,识别图中二维码即可关注


相关阅读

  • IntelliJ IDEA 新版本特性

  • 点蓝字关注,一起程序员弯道超车之路IntelliJ IDEA 2022.3 正式发布,在新版本中,开发者可以通过设置切换到新 UI,即可预览新的 IDE 外观。此版本引入了一个新的 Settings Sync(设
  • 跳出“复制粘贴”的无尽循环,自己 DIY 一个 Babel

  • 前言问题我们在使用各种打包工具,需要配置Babel的时候,相信大家一开始都是直接在网上复制粘贴一段配置过来,然后能跑通就万事大吉了吧?因此,我们有时会遇到打包部署后,手机运行出
  • 每日安全动态推送(2-28)

  • Tencent Security Xuanwu Lab Daily News• [Tools, Web] Toxssin – Open-source Penetration Testing Tool That Automates Exploiting Cross-Site Scripting (XSS):https
  • 测试左移-快速玩转Debug

  • 背 景一段代码的问题产生阶段可以分为:编译期和运行时编译期的代码可以由工具(idea、eclipse)在程序编码过程中提示错误,解决了语法上的标准问题运行时的代码在程序启动、运行过
  • 11个JavaScript 单行代码技巧

  • 编辑整理 | 杨小爱 我们每个 JavaScript 程序员都应该学习使用 JavaScript 单行代码技巧来提高生产力,因此,今天这篇文章,我们将分享一些可以在日常开发生活中使用的单行代码技
  • 【每日一练】107—一款好玩的注册登录UI效果

  • 文 | 杨小爱写在前面注册登录的组件,在前面也分享过一些,今天我们再来看一个注册登录的UI样式效果,截图如下:为了能方便看到鼠标效果,我录了一个GIF的动图,如下:看完了实现后的效果
  • 锡常TOP新人分享

  • 本期TOP新人分享分享嘉宾:无锡UA安希伦入职时间:8月9日,20多个工作日业绩情况:3单,共21694元签单客户:纯新客户GreaterSnow一、入职不到一个月已经开了3单,都是纯新客户,你的纯新客

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • Kotlin协程源码简单聊一聊,浅浅看看表层

  • / 今日科技快讯 /近日,经历了一次停办,两次规模严重缩水之后,2023年世界移动通信大会(MWC)终于再现往日盛况:有来自200多个国家和地区的2000多家厂商参加,并在此次展会上发布了
  • 河北:服务个体户 助力稳增长

  • 今年以来,河北各地鼓励、支持和引导个体经济健康发展,进一步激发市场活力,发挥个体工商户在稳就业、促增长中的基础作用和关键作用,促进个体工商户持续健康发展。“我盘下的店是
  • 不可错过的异常监控工具!

  • 近年来,小程序逐渐从一个风口发展为了企业软件轻业务板块的标配入口,从赛道发展为了渠道。即便是在2022年的互联网寒冬中,小程序仍然保持着蓬勃的发展,仅2022年H1,微信、支付宝、
  • 延吉市妇女第十三次代表大会代表热议工作报告

  • 3月1日,参加延吉市妇女第十三次代表大会的代表们分团讨论了《延吉市妇女联合会第十二届执行委员会工作报告》和市委常务副书记薛志强、州妇联主席韩丽莲的讲话。  各代表团