coroutines 学习随笔「一」

前言

前几天开始正式学习研究coroutines了 因为之前一直处于会用的状态很多东西都是懵懂的
以下研究基于idea环境


疑问1.为啥用协程还要添加一个额外的依赖库 太奇怪了
    // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
    implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.5.1'

suspend关键字不是kotlin里面提供的吗 那么我不加依赖行不行 ?带着这个疑问我开始了研究

我的runBlocking,launch,withContext 等等都哪去了?

为了方便对照 我又开了新的kotlin application项目加上以上的依赖 最终发现 那些方法都是 kotlinx.coroutines 包里面的

这和我的预期完全不符
提供了关键字但是标准库又不做任何底层支持 这实在太奇怪了

跟踪到标准库去看看

标准库也不是什么都没有 它里面定义了以下(按照继承关系进行了对齐)

  • CoroutineContext
    • Element
      • ContinuationInterceptor
      • AbstractCoroutineContextElement
    • CombinedContext
  • Key
  • Continuation

那么问题来了:所以协程他是kotlin标准库定义了一部分 然后又用其他库来拓展api吗?
我带着这个问题找了个最简单的api launch
在这里插入图片描述
我们传入了一个 方法 然后他就把这个方法 start 起来了
跟踪到start方法
在这里插入图片描述
我相信绝大多数人都不例外 看得懵懵的
又是泛型,又是带泛型的函数入参
而且最坑的是想要继续跟踪里面的start方法 一点击还又给你指向到原地
正确的做法是点击那个括号 如下图 windows 上 ctrl+鼠标左键
在这里插入图片描述
因为这是一个操作符重载
在这里插入图片描述
在这个invoke方法中 这个this就是 之前 launch方法中传入的start对象 他是有个默认值的
在这里插入图片描述
所以他会走到 block.startCoroutineCancellable(receiver, completion) 的分支 这个方法他是一个函数的拓展,需要提及的一下就是直到这里这里仍然还是 kotlinx中的协程的api
在这里插入图片描述
runSafely当前来讲不用管 它就是再包了一层 做了个异常处理 我们进入到createCoroutineUnintercepted 方法中 进入到这个方法还有点复杂 我们稍后再细说
在这里插入图片描述
到了这里来小结一下:

  1. 调用launch方法 里面构造了一个 StandaloneCoroutine或者LazyStandaloneCoroutine(StandaloneCoroutine的子类)
  2. 然后调用其start方法 这个start方法定义在他的父类之中 AbstractCoroutine
  3. 在这个start方法中 调用了 CoroutineStart的操作符重载的start方法
  4. 在这个操作符方法(start)里面调用了 一个suspend函数的拓展方法 startCoroutineCancellable
  5. 在startCoroutineCancellable中
createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit), onCancellation)
从现在开始进入到标准库了

createCoroutineUnintercepted方法的定义在kotlin.coroutines.intrinsics.IntrinsicsJvm.kt:118中
在这里插入图片描述
代码很简单啊 根据类型 分了两个分支逻辑 但是我们好好想一下 这个this是什么 它就是我们的block啊
在这里插入图片描述

那么这个代码为什么要这样写呢 if( block is BaseContinuationImpl )

这不可能为true啊!!! 难道说它在什么地方做了什么奇怪的魔术吗
点击这里查看为true的分支
那我们就先看看false的分支
在这里插入图片描述
在这个方法中他也是会返回 BaseContinuationImpl 的实例对象 如下图 未折叠版大图看这里
在这里插入图片描述
这个回调就会随后在其 BaseContinuationImpl 的实例对象 的invokeSuspend方法中调用
在这里插入图片描述

下个小结论

我们的block被强制转换成了Function2然后执行了invoke方法 整个流程就完成了
launch-> StandaloneCoroutine::start -> CoroutineStart::start -> block::startCoroutineCancellable -> block::createCoroutineUnintercepted ->
block强制转换成 Function2然后 invoke

好 那么问题来了它把this (也就是block: suspend CoroutineScope.() -> Unit) 强制转换成了 Fuction2
黑人问号脸???
我记得他的方法签名是 suspend CoroutineScope.() ->Unit 换算一下就是
suspend T.() -> T2
而Function2的签名为 Fuction<T1,T2,T3>换算成lamda就是 T1.(T2) -> T3

suspend T.() -> T2
vs
T1.(T2) -> T3

你确定这能转换吗
在这里插入图片描述
做个小demo 定义一个suspend方法的的函数对象 然后反编译看看
咱们定义的还真被编译成了Fuction2 而且还给我们添加几个额外的方法 invokeSuspend和create ,invoke方法是对Function2的实现 所以上面的强制转换是合理的

suspend 方法反编译的代码:
在这里插入图片描述

查看false分支

this is BaseContinuationImpl 可能为true吗

我们在此处打个断点调试看看
在这里插入图片描述
在控制台分别调用 来查看他的类结构
this.javaClass.superclass
this.javaClass.interfaces
在这里插入图片描述
得到结果是:
this.javaClass.superclass => class kotlin.coroutines.jvm.internal.SuspendLambda
this.javaClass.interfaces => interface kotlin.jvm.functions.Function2
interfaces的结果我们能够理解 因为在false的逻辑中我们也已经证明这一点
但是他的父类是 SuspendLambda 我就不能理解了 难道说 supsend修饰的高阶函数是隐形中默认继承了SuspendLambda的吗 (关于这一点我目前还无法证明)如果是的话 那么一切就都说得通了

它调用自己的 create方法 在这里插入图片描述
而这个create方法在哪里呢 就在 这儿

回到问题:为啥用协程还要添加一个额外的依赖库 不加行不行

通过之前的分析 我们知道了 协程是通过一个特殊的 拓展方法创建启动的 而这个方法是定义在标准库的
在这里插入图片描述
为啥用协程还要添加一个额外的依赖库 不加行不行?
那么结论就是 当然可以了 我们自己调用这个方法不就行了吗
这个方法它间接的调用了createCoroutineUnintercepted方法
在这里插入图片描述

本文完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值