本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布[2023-03-02]
前言: 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官网看,太深了,我没看,我找到了 提供给需求的人!
这儿看看阻拦的办法
能够看出,此刻就会分发 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创立协程,需求手动康复
这四胞胎说实话,是真没啥用,但便是得了解一下.. 看的头疼:)
完整代码
原创不易,您的点赞便是对我最大的支持!