文章目录
前言
前几天开始正式学习研究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
- Element
- Key
- Continuation
那么问题来了:所以协程他是kotlin标准库定义了一部分 然后又用其他库来拓展api吗?
我带着这个问题找了个最简单的api launch
我们传入了一个 方法 然后他就把这个方法 start 起来了
跟踪到start方法
我相信绝大多数人都不例外 看得懵懵的
又是泛型,又是带泛型的函数入参
而且最坑的是想要继续跟踪里面的start方法 一点击还又给你指向到原地
正确的做法是点击那个括号 如下图 windows 上 ctrl+鼠标左键
因为这是一个操作符重载
在这个invoke方法中 这个this就是 之前 launch方法中传入的start对象 他是有个默认值的
所以他会走到 block.startCoroutineCancellable(receiver, completion) 的分支 这个方法他是一个函数的拓展,需要提及的一下就是直到这里这里仍然还是 kotlinx中的协程的api
runSafely当前来讲不用管 它就是再包了一层 做了个异常处理 我们进入到createCoroutineUnintercepted 方法中 进入到这个方法还有点复杂 我们稍后再细说
到了这里来小结一下:
- 调用launch方法 里面构造了一个 StandaloneCoroutine或者LazyStandaloneCoroutine(StandaloneCoroutine的子类)
- 然后调用其start方法 这个start方法定义在他的父类之中 AbstractCoroutine
- 在这个start方法中 调用了 CoroutineStart的操作符重载的start方法
- 在这个操作符方法(start)里面调用了 一个suspend函数的拓展方法 startCoroutineCancellable
- 在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 方法反编译的代码:
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方法
本文完。