作者
大家好,我叫
自己于2020年10月参与37手游安卓团队
现在主要担任国内相关业务开发和一些日常业务
布景
运用kotlin的协程一段时刻了,常用的用法也现已很熟悉,但都是java初学停留在运用的阶段,没有对代码深入了解过,仍是感觉有点虚;趁着春节这段时刻,针对协程的反常处理,对其相关的源码学习javascript了一波,收拾总结一下自己的了解。
本文依据 Kotlin v1.4.0,Kotlin-Coroutines v1.3.9源码剖析
1、CoroutineScope源码剖析
效果:创立和寻找协程,管理不同协程之间的父子关系和结构
创立协程的办法:
1、经过CoroutineScope创立
2、在协程中创立
第一种办法,首要怎样经过CoroutineScope创立?
val scope = CoroutineScope(Job() + Dispatchers.Main)
@Suppress("FunctionName")
public fun CoroutineScope(contextjava初学: CoroutineContext): CoroutineScope =
ContextScope(if (context[Jelementaryob] != null) context else context + Job()) //没有job实例的话就搞一个
internal class ContextSc宫崎骏ope(context: CoroutineContext) : CoroutineScope {
oelementary是什么意思verride val corouapplicationtineContext: CoroutineContext = context
}
CoroutineScope是一个大局的方工商银行法,然后在枸杞里边经过ContextScope就能够实例elementary翻译出来一个CoroutineKotlinScope政策了。
相似我们平常用到的MainScope或许Android平台上viewMkotlin极简教程odelScope和lifecycleScope(只不过在生命周期相关回调做了有些自动cancel的处理)
也是跑到这儿来。其他scope初始approve化的时分会有生成一个job,起到盯梢的效果
这儿需求留心的是GlobalScope和普通协程的CoroutineScope的差异,GlobalScope的 Job 是为空的,由于它的coroutinejava面试题Context是ElementEmptyCoroutineContext,是没有job的
有了scope之后,我们就能够经过launch创立一个协程了
val job = selementarycope.launch {}
戳代码看看
public fun CoroutineScope.launch(
context: CoroutineContext = EKotlinmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
。。。省掉java初学代码。。。
retjava训练urn coroutine
}
launch参数有三个,前两个参数先不不剖析,第三个是一个带receiver的lambda参数(参阅Kotlin 中的Receiver 是什么),默kotlin言语许的类型是CoroutineScappearanceope
val job = scope.launch {appear①/* this: CorouapplicationtineScope */
// 新的协程会将 CoroutineSkotlin ?.效果cope 作为父级APP ,在launch里边创立
//由于launch是一个扩展办法, 所以kotlin教程上面比如中默许的receiver是this,appreciate所以approve以下两种写法相同。这工商银行儿能够了解为这Go儿是一个回调,句柄是CoroutineScop
launch { /* ... */ }
this.launch {
// 经过 ① 创立的新协程作为当时APP协程的父级
}
}
再appear看看CoroutineScope.laukotlin与javanch的完毕
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start:狗狗币 CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() ->kotlin极简教程 Unit
): Job {
//这儿是依据父级创立新的上下文(协程的父级上下文),然后给下面创立协程用,具体逻辑下面代码块剖析
val newContext = newCoroutineCokotlin面试题ntext(context)
//这儿便是创立协程
val coroutine = if (start.isLazy)
//协程真实的上下文生成是以newContext作为父级上下文生成的javascript
LazyStandalonelementanimationeCoroutine(newContext, block) else
StandalGooneCoroutine(newContext, active = true)
//start里边便是创立job相关的,不同的coroutine实例有不同的生成job战略
coro作业细胞utine.start(stJavaart,java游戏 coroutine, block)
return coroutine
}
public actual fu公积金n CoroapproachutineScope.newCoroutineCAPPontext(context: CoroutineContext): CoroujavascripttineContext {
//原本这是一个CoroutineScope的扩展函数,coroutineContext其实便是拿到到了sGocope政策的成员,然后经过“+”就能够搞javahihi2018成了,下面会说“+”
//能够了appreciate解为把一个context数据add到一个 context map数据组中elementary怎么读音,还有一些逻辑判别,先不论,横竖拿到的是一个新的context map
val cojava模拟器mbined = coroutineContext + context
//检验环境会给一下id拿来调试用的
val debug = if (DEBUG) combi工商银行ned + CoroutineId(COROUTINE_ID.incrementAndGejava模拟器t()) elappleidse combined
return if (combkotlin发音ined !== Dispatchers.Default &&aElementmp; combined[ContinuationInterceptor] == null)
debug +google Dispatchers.Default else debug
}
“+” 怎样相加的?这就涉及到的相关类
Coroutiapp装置下载neContext: 全部上下文的接口
CombinedContext:上下文组合时生成的类
CoroutineContext.Elkotlin发音ement:大部分单个上下文完毕的类,由于有的会直接完毕Corokotlin实战utineContext
public operatorappearance fun plu枸杞s(context: CoroutineContext): Coroutinelement滑板eContext =
//operator操作符重载的特性 eg:J龚俊ob() + Dispatchers.IO + CoroutineName("test") 就会跑到这儿来
if (co作业总结ntext === EmptyCoroutineContext) this elelementary是什么意思se // fakotlin极简教程st path -- avoid lambda creation
//acc为加数,element为被加数
context.fold(this) { acc, element ->
val removed = acc.minusKey(element.key)
if (removed === EmptyCoroutineContext) element else {
// make sure interceptor is always last in the context (andjava模拟器 thus is fast to get when present)
val intercepelement滑板tor = removed[ContinuationInterceptor]
if (java训练interceptor == null) CombinedCo工商银行ntext(removed, element) else {
val left =app装置下载 removed.minusKey(ContinuationInterceptor)
if (left === EmptyCoroutineContext) CombinedContext(element,application interceptor) else
CombinedContext(CombinedContext(left, element), interceptor)
}
}
}
能够了解为一个map(实际上是一个单链表,具体的能够参阅Kotlin协程上下文CoroutineContext是怎样可java是什么意思相加的),经过key来获取不同类型的数据,需求改变的话运用当时的CoroutineContext来创立一个新的CoroutineContext即可。
上面说到的val sc龚俊ope = CelementanimationoroutineScope(Job() + Dispatchers.MKotlinain)
综和以上两个代码片段,咱kotlin极简教程们能够知道,一个新建的协程CoroutineContext的java模拟器元素组成
1、有一个元素job,操控协程的生命周期
2、剩余的元素会作业总结从CoroutineContext 的父级承继,该父级可能是其他一个协程或许创立该协程的 CoroutineScope
2、CoroutineScope的类型
2.1、协程效果域对javahihi2018反常传播的影响
阐明:
C2-1产生反常的时分,C2-1->C2-kotlin面试题>C2-elementary翻译2->C2->C1->C3(包含里边的子协程)->C4
C3-1-1产生反常的时分,C3-approve1-1->C3-1-1-1,其他不受影响
C3-1-kotlin教程1-1产生反常的时分,C3-1-1-1->C3-1-1appstore,其他不受影响
2.2、示意代码
1、C1和C2没有关系
GlobalScope.launch { //协程C1
GlobalScope.lapplicationaunch {//协程C2
//...
}
}
2龚俊、C2和C3是C1的子协程,C2和C3反常会撤消C1
GlobalScope.launch { //协程C1
coroutineScoope {
launch{}//协程C2
launch{}//协程C3
}
}
3、C2和C3是C1的子协程,C2和C3反常不会撤消C1
GlobalScope.launchelementary { //协程C1
supervisorScope {
laukotlin发音nch{}//协程C2
launch{}//协程C3
}
}
2.3、举个
eg1:
@Test
fun test()= runBlocking{
val handler = CoroutineExceptionHandler { coroutineContext, exception ->
println("CoroutineExceptionHandler got $exception coroutineContext ${coroutineCon宫颈癌text}")
}
val job = GlobalScope.launch(hanjava训练dler) {
println("1")
delay(1000)
coroutineScope {
println("2")
val job2 = launch(hanapproachdler) {
throwErrorTest()
}
println("3")
job2.joielementuin()
prkotlin面试题intln("4")
}
}
job.join()
}
fun throwErrorTest(){
throw Exception("error test")
}
假element滑板设是协同效果域,job2地址的协程产生反常,会kotlin教程把job撤消(不会打印“4”),并且反常是从job地址协程抛出来的
eg2:
@Test
fun test()= runBlocking{
val handler = CoroutiappstoreneExceptionHandler {java模拟器 coroutineContext, exception ->java是什么意思
printkotlin言语ln("CoroutineExceptionHandler got $exception coroutineContext ${coroutineContext}")
}
val job = Glo作业总结balScope.launch(handler) {
p作业细胞rintln("1")
delay(1000)
supervisorScope {
println("2")
val job2 = launch龚俊(handler) {
throwErrorTest()
}
println("公积金3")
job2.join()
println("4")
}elementui
}
job.join()
}kotlin发音
fun throwErrorTest()kotlin下载{
throw Exception("error test")
}
输出效果:
假定是主从application效果域,joappearb2地址的协程产生反常,不会把job撤消(会打印“4”),并且反常是job2地址协程抛出来的
3、协程中反常处理的流程源码剖析
3.1、协程的三层包装
第一层:launch和async回来的job,封装了协程的情况,供给撤消协程的接口,实java游戏例都是承继自Abstracelementary是什么意思tCoroutine
第二层:编译器生成(cps)的SuspendLambda的子类,封装了协程的真实运算逻辑,承继自BaseContinuationImpl,其间completion特点便是协程的第一层kotlin实战包装
第三层:DispatchedContinuatkotlin为什么流行不起来ion,封装了线程调度逻辑,包含了协程的第java游戏二层包装
三层包装都完毕了Continuation接口,经过署理形式将协程的各层包装组合在一起,每层担任不同的功用
运算逻辑在第二层BaseContinuationImpl的resumeWith()函数中的invokeSuspend工作
3.2、产生反常的进口
BaseContinuationImpl中的resumeWith(result: Result<Any?>)处理失appear常的逻辑,省掉的部分代码
pubAPPlic final overkotlin言语ride fun resumeWith(result: Result) {
val completiAPPon = completion!! // fail fast when trying to resume continuation without completion
val公积金 outcome: Result =
。approve。。其他代码。。。
try {
val outcome = invokeSuspend(param)
if (outcoElementme === COROUTINE_SUSPENDED) return
Result.success(outcome)
} catch (exception: Throwable) {
Result.failure(exception)
}
。。。其他代码。。。
complelementary怎么读音etion.resumeWikotlin与javath(outcome)
。。。其他代码。。。
}
由以上代码剖析可知
1、invokeSuspend(par作业总结am)办法的具体完毕是在编译的生成的,对应协程体的处理kotlin面试题逻辑
2、当产生反常的时分,即outcome为Result.failure(excekotlin极简教程ption),具体调用在completion.resumeWith(outcome)里边,经过AbstractC公积金oroutine.resumeWith(Result.failure(exception))进入kotlin教程到第三层包装中
继续盯梢 AbstractCoroutine.resumeWith(result: Result) -> JobSupport.makeCompletingOnce(proposedUpdate: Any?): Any? -> JobSupport.tryMakelementary翻译eCompletinelementary怎么读音g(state: Any?, proposedUpdate: Any?): Any?->JobSupport.tryMakeCkotlin发音ompletingSlowPath(state: Incomplete, proposedUpdate: Any?): Any?
在tryMakeCompletingSlowPath办法中
var notifyRootCause: Throwable? = null
synchronizelementary翻译ed(finishing) {
//。。。其他代码。。。
notifyRootCause = fjava训练inishing.rootCause.takeIf { !wasCancelling }
}
// process cancell宫颈癌ing notification here -kotlin ?.效果- it cancels all the children _before_ we starjava是什么意思tjava面试题 to to wait them (sic!!!)
// 该景象下,notifyRootCause 的值为 exceJavaption
notifyRootCause?.let { notifyCancelling(list, it) }
// otheappearancerwise -- we have not children left (all were already cancelljava难学吗ed?)
return finkotlin ?.效果alizeFKotlininishingState(finishing, proposedUpdate)
//。。。其他代码。。。
假定产生反常即notifyRootCaElementuse不为空的时分,调用notifyCancelelementary是什么意思ling办法approach,主要是撤消子协程
pappearrivate fun notifyCancelling(list: NodeList, cause: Throwable) {
// first cancel our own children
onCa狗狗币ncelling(cause)
notifyHandlers>(list, cause)
// then cancel parent
cancelParent(cause) // tentatjava难学吗ive cancellation -- does not matter if there is no parent
}
其他elementary一个办法finalizeFinishingState,主要是反常传递和处理的逻辑,要害代码如下
private fun fijava是什么意思nalizeFinishingState(state: Finappreciateishing, proposedUpdate: Any?): Anjava训练y? {
。appstore。。其他代码。。。
// Now handle the final exception
if (finalE枸杞xception != null) {
//反常的传递和处理逻辑,假定cancelParent(finalEkotlin下载xception)不处理反常的话,就由当时
//协程处理handleJobException(finalException)(具体完毕在Standalonejava模拟器Coroutine类处理反常,下文会说到)
va作业总结l handled = cancelParent(finalException) || handleJobException(finalException)
if (hanjava游戏dled) (finalState as CompletedExjava训练课程ceptionally).makeHandled()
}
。。其他代码。。。
return finalState
}
/**
* Tkotlin实战he mkotlin言语ethoapproved that is ielementanimationnvoked when tapplicationhe job is cancelleappled to possibly propagate cancellation to the parent.
* Returns `true` if the parent is respkotlin言语onsible for handling the exception, `false` otheelement滑板rwise.
*
* Invariant: ne龚俊ver returns `false` for instances of [Cancellati枸杞onelementary翻译Exception], otherwise such exception
* may leak to the [CoroutineExceptionHandler].
* 回来指是true的话,反常由父协龚俊程处理,false的话反常由地址的协程来处理
*/
private fun cancelParent(cause: Throwable): Boolean {
// Is scoped coroutine -- don't propagate, wkotlin发音ill be rethrown
/**
* Reelementsturns `true` for scoped coroutines.
* Scoped coroutine is a coroutine that is executed sequentiallyapprove within the enclosing scope without any concurrency.
* Scoped coroutineselementary always handle any exception happened within -- they just rethrow it to theelement是什么牌子 enclosing scope.
* Examples of scoped coroutines are `coroutineScope`, `withTimeout` aappearancend `runBlocking`.
*/
//假定isScopedCoroutine true的话,即coroutineScope是主从效果域的话,反常是会传到父协程
if (isScopedCoroutine) return true
//ca枸杞use是CancellationException的话是正常的协程完毕行为,不会撤消父协程
/*appear CancellationException is considered "normal" and parent usually is not cancelled when child produces it.
* This allow parent to cancel it公积金s children (noelementary怎么读音rmally) without being cancelled itself, unless
* chielementsld crashes and producelementuie some other exception during its completion.
*/
val isCancellation = caapproachuse is CancellationException
val parentkotlin言语 = parentHandle
// No parent -- ignore CE, report other exceptioapproachns.
if (parent === null || parent === NonDisposableHandle) {
return isCancellation
}
// Notijava是什么意思fy parent but don't forget to checelement是什么牌子k cancellation
//chilKotlindCancelled(cause)为false的话,反常不会传递到父协kotlin教程程
//运用SupervisorJob和supe龚俊rvisorScope时,子协程出现未捕获反常时也不会影响父协程,
//它们的原理是重写 childCancelled() 为oveelementary是什么意思rride fun childCancelled(cause: Throwable): Boolean = false
return parent.childCancelled(cause) || isCancellation
}
由以上代码elementui可知
1、出现未捕获反常时,首要会宫颈癌撤消全部子协程
2、反常归于 CancellationException 时,不会撤消父协程
3、elementary运用SupervisorJob和supervisorScope时,即主从效果域,产生反常不会撤消父协程,反常由地址的协程处理
3.3、CoroutineExceptappstoreionHandl宫崎骏er的是怎样收效的
在AbstractCoroutine中,处理反常的逻辑是在JobSupport接口中,默许是空的完毕。
protected open funJava handleJobException(exception: T枸杞hrowable): Boolean = false
具体的完毕逻辑是在StandaloneCoroutine中(Builders.common.kt文件)
private oappearancepen claselementarys StandaloneCoroutine(
parentContext: CoroutineContext,
active: Boolean
) : Abstrkotlin言语actCoroutine(parentContext, active) {
ovejavascriptrride fun handleJobExcepjava面试题tion(excepti宫颈癌on: Throwable): Boolean {
//处理反常的逻辑
handleCoroutineException(conteelementuixt,APP exception)
return true
}
}
具体完毕如下
//CoroutElementiAPPneExceptionHandlerImpl.kt
private val handlers: List = ServiceLoader.load(
CoroutineExceptionHandler::class.java,
Corjava训练课程outineExceptionHandler::class.java.classLoader
).iterator().asSequence().tojava模拟器List()
internal actual fun handleCoroutelementary翻译ineExceptapplicationionImpl(context: CoroutineContexjavahihi2018t, exception: Throwable) {
// use additional extension handlers
for (handler in handlers) {
try {
handler.handleException(context, excep龚俊tion)
} catch (t: Throwable) {
// Use thread's handler if custom handler failed to handle exception
val currentThread = Thread.currentThread()
cappreciateurrentThread.uncaughtExceptionHandler.uncaughtException(currentThread, handlerException(exception, t))
}
}
// 调用element是什么牌子当时线程的 uncaughtExceptionHandler 处理反常
// use thread's happleandler
val currentThread枸杞 = Thread.currentThread()
cjava训练urrekotlin ?.效果ntThread.uncaughtExceptionHandler.uncaughtExceptioappreciaten(curren狗狗币tThread, exception)
}
以上的处理逻辑能够简略的归纳为以下伪代码
class StandardCoroutine(context: CoroutineContext) : AbstractCoroutine(context) {
override fun handleJobException(e: Throwable): Boolean {
context龚俊[CoroutineExceptionHandler]?.handleException(context, e) ?:
Thread.currentThread().let { it.uncaughtkotlin极简教程ExceptionHandler.uncaughtException(it, e) }
return true
}
}
所以默许情况下element什么意思中文,launch式协程对未捕获的反常只是打印反常库房信息,假定运用了 CoroutineExceptionHandler 的话,只会运用自定义的 CoroutineExceptionHandler 处理反常。
小结
1、协程默许的效果域是协同效果域,反常会传播到父协程处理,即coroutineScope或许appreciateCoroutineScope(Job()java面试题)这种方法。
2、协程效果域假定是主从效果域,反常不会传播到父协程处理,即supervisorScope 或 CoroutineScope(SupervisorJob()) 这种方法,其要害是重写 childCancelled()=fkotlin发音alse。
3、协程处理反常的时分,假定自定义CoroutineExceptionHandler的话,则由其处理,否则交给体系处理。
毕竟,本文反常处理剖析是从协程效果域为切入点进行的,看代码过程中也javahihi2018会学到一些kotkotlin下载lin美妙的语法运用;其他只是大约的去剖析了一下反常的处理主线逻辑,有些细节的还需求去继续学习,下次会进行愈加具体的剖析,希望本文对你有协助,也欢迎一起交流学习。