欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

kotlin 協(xié)程上下文異常處理詳解

 更新時(shí)間:2022年08月31日 16:02:41   作者:aruba  
這篇文章主要為大家介紹了kotlin 協(xié)程上下文異常處理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

從前面我們可以大致了解了協(xié)程的玩法,如果一個(gè)協(xié)程中使用子協(xié)程,那么該協(xié)程會(huì)等待子協(xié)程執(zhí)行結(jié)束后才真正退出,而達(dá)到這種效果的原因就是協(xié)程上下文,上下文貫穿了協(xié)程的生命周期,這套思想和我們app的上下文很像

在開始真正了解協(xié)程上下文之前,我們先來看看下面的例子

下面的圖代表了一個(gè)協(xié)程a的生命,就像一條從上至下的直線,它的生命只有100ms

當(dāng)我們在a協(xié)程延遲函數(shù)100ms之前開啟一個(gè)子協(xié)程b,b做了200ms的事情,如果不考慮調(diào)度消耗的時(shí)間,那么a協(xié)程的生命也會(huì)延長成200ms

代碼驗(yàn)證下:

fun `test context life`() = runBlocking {
    //定義一個(gè)作用域
    val a = CoroutineScope(Dispatchers.Default)
    val startTime = System.currentTimeMillis()
    //協(xié)程a開啟
    val jobA = a.launch {
        //子協(xié)程b開啟
        val jobB = launch {
            delay(200)
        }
        delay(100)
    }
    //等待協(xié)程a結(jié)束
    jobA.join()
    val endTime = System.currentTimeMillis()
    println(endTime - startTime)
}
fun main() {
    `test context life`()
}

結(jié)果:237

如果我們把子協(xié)程b增加到delay 300ms,那么結(jié)果也會(huì)相應(yīng)的變?yōu)椋?/p>

323

通過上面的列子,來對協(xié)程上下文的有一個(gè)初步概念:可以說協(xié)程的生命周期,就是上下文的生命周期

協(xié)程擁有很多新的概念,很多人一開始接觸就能難理解(包括我自己),這些概念都是在上下文的基礎(chǔ)上引申而來的,所以我一再強(qiáng)調(diào)它的重要性,協(xié)程的上下文必須理解透,才能玩好協(xié)程,接下來我們來真正了解協(xié)程上下文

一、協(xié)程上下文

1.CoroutineContext

協(xié)程上下文有以下幾項(xiàng)構(gòu)成,它們都是實(shí)現(xiàn)了CoroutineContext.Element接口,有些是實(shí)現(xiàn)了AbstractCoroutineContextElement接口,而AbstractCoroutineContextElement繼承CoroutineContext.Element接口

1.Job:控制協(xié)程的生命周期,也是我們能拿到操作協(xié)程任務(wù)的唯一對象

2.CoroutineDispatcher:就是之前介紹的調(diào)度器

3.CoroutineName:協(xié)程的名字,一般輸出日志用的

4.CoroutineExceptionHandler:處理未捕獲的異常

協(xié)程上下文實(shí)現(xiàn)了運(yùn)算符重載,我們可以用+號來組合一個(gè)CoroutineContext的元素

2.CorountineScope

一般情況下,協(xié)程體內(nèi)所有的子協(xié)程,都繼承至根協(xié)程,協(xié)程的繼承的關(guān)系不是我們所了解的類的繼承關(guān)系,而是父協(xié)程和子協(xié)程的生命周期關(guān)系,還記得我們上面舉得例子么,除非在協(xié)程體內(nèi)自己手動(dòng)創(chuàng)建協(xié)程作用域,即:創(chuàng)建一個(gè)全新的協(xié)程上下文,我們之前已經(jīng)介紹過了:

CorountineScope:創(chuàng)建協(xié)程作用域,新起線程,觀察源碼,內(nèi)部實(shí)際實(shí)例化的是ContextScope,ContextScope被internal修飾,內(nèi)部使用,我們實(shí)例化不了

其他的實(shí)際上都是繼承父協(xié)程上下文,或者內(nèi)部實(shí)例化了ContextScope:

1.runBlocking:將主線程轉(zhuǎn)變?yōu)閰f(xié)程,會(huì)阻塞主線程,實(shí)際上用的是一個(gè)EmptyCoroutineContext作為上下文,它是一個(gè)主線程的協(xié)程上下文,靜態(tài)的全局變量,我們其實(shí)就可以理解成是主線程
2.GlobalScope:也是用的EmptyCoroutineContext
3.MainScope:使用ContextScope構(gòu)造了新的上下文
4.coroutineScope:繼承的父協(xié)程上下文,不能算是全新的協(xié)程
等等

3.子協(xié)程繼承父協(xié)程

子協(xié)程繼承父協(xié)程時(shí),除了Job會(huì)自動(dòng)創(chuàng)建新的實(shí)例外,其他3項(xiàng)的不手動(dòng)指定的話,都會(huì)自動(dòng)繼承父協(xié)程的,Job對應(yīng)的是協(xié)程任務(wù),每次新的任務(wù)肯定都是新的Job對象

有了這些概念后,接下來通過代碼,再熟悉鞏固下

例子1:

fun `test context life1`() = runBlocking {
    //定義一個(gè)作用域
    val a = CoroutineScope(Dispatchers.Default)
    //協(xié)程a開啟
    val jobA = a.launch {
        delay(100)
        println("jobA finished")
    }
    println("main finished")
}

結(jié)果:
main finished

由于a是一個(gè)根協(xié)程,全新的上下文,runBlocking 是主線程的協(xié)程上下文,所以當(dāng)a開啟任務(wù)時(shí),不會(huì)阻塞主線程,當(dāng)我們的進(jìn)程都跑完了,jobA finished肯定不會(huì)打印了

例子2:

fun `test context life2`() = runBlocking {
    //定義一個(gè)作用域
    val a = CoroutineScope(Dispatchers.Default)
    //協(xié)程a開啟
    val jobA = a.launch {
        delay(100)
        println("jobA finished")
    }
    jobA.join()
    println("main finished")
}

結(jié)果:
jobA finished
main finished

我們在主協(xié)程(主線程的協(xié)程)中,手動(dòng)調(diào)用jobA的join方法,那么主線程就會(huì)阻塞,直到j(luò)obA執(zhí)行完畢。這個(gè)和我們的多線程操作是一樣的,主線程等待A線程執(zhí)行完后再往后執(zhí)行

例子3:

fun `test context life3`() = runBlocking {
    launch {
        delay(100)
        println("jobA finished")
    }
    println("main finished")
}

結(jié)果:
main finished
jobA finished

這回我們沒有構(gòu)建新的協(xié)程作用域,而是在根協(xié)程中直接使用子協(xié)程的方式,當(dāng)然了,協(xié)程的上下文繼承關(guān)系,使得我們的主協(xié)程等待子協(xié)程執(zhí)行完畢后才結(jié)束生命

例子4:

fun `test context life4`() = runBlocking {
    launch(Dispatchers.IO + CoroutineName("jobA")) {
        delay(100)
        println("${coroutineContext[CoroutineName]}  finished")
    }
    println("main finished")
}

結(jié)果:
main finished
CoroutineName(jobA) finished

即使我們指定了子協(xié)程的調(diào)度器和協(xié)程名,也不會(huì)影響協(xié)程上下文繼承關(guān)系,主協(xié)程還是會(huì)等待子協(xié)程執(zhí)行完畢后才結(jié)束生命

如果你已經(jīng)完全理解了,那么就可以知道以上例子使用async啟動(dòng)也是一樣的效果

二、協(xié)程的異常傳遞

1.協(xié)程的異常傳播

協(xié)程的異常傳播也是遵循了協(xié)程上下文的機(jī)制,除了取消異常(CancellationException)外,當(dāng)一個(gè)協(xié)程有了異常,如果沒有主動(dòng)捕獲異常,那么異常會(huì)向上傳播,直到根協(xié)程,子協(xié)程的異常都會(huì)導(dǎo)致根協(xié)程退出,自然其他子協(xié)程也會(huì)退出

例子1:

fun `test coroutineScope exception1`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = launch {
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
    }
    delay(3000)
    println("finished")
}

結(jié)果:

job2 finished
Exception in thread "main" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception1$1$job2$1.invokeSuspend(exceptionTest.kt:46)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.ResumeModeKt.resumeMode(ResumeMode.kt:67)
    at kotlinx.coroutines.DispatchedKt.resume(Dispatched.kt:309)
    at kotlinx.coroutines.DispatchedKt.dispatch(Dispatched.kt:298)
    at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:250)
    at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:260)
    at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:332)
    at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.kt:298)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.kt:116)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:80)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt.test coroutineScope exception1(exceptionTest.kt:37)
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt.main(exceptionTest.kt:54)
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt.main(exceptionTest.kt)

Process finished with exit code 1

job2 1000ms后就發(fā)生了異常,導(dǎo)致job1和父協(xié)程都直接退出

2.不同上下文(沒有繼承關(guān)系)之間協(xié)程異常會(huì)怎么樣?

例子1:

fun `test coroutineScope exception2`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = CoroutineScope(Dispatchers.IO).launch{
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
        println("new CoroutineScope finished")
    }
    delay(3000)
    println("finished")
}

結(jié)果:

job2 finished
Exception in thread "DefaultDispatcher-worker-2" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception1$1$job2$1.invokeSuspend(exceptionTest.kt:46)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
job finished
finished

Process finished with exit code 0

可以看出不同根協(xié)程的協(xié)程之間,異常并不會(huì)自動(dòng)傳遞,我們的主線程上下文協(xié)程正常執(zhí)行

再看例子2:

fun `test coroutineScope exception3`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = CoroutineScope(Dispatchers.IO).async{
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
        println("new CoroutineScope finished")
    }
    delay(3000)
    println("finished")
}

結(jié)果:
job2 finished
job finished
finished

和例子1的唯一區(qū)別是,使用了全新上下文的協(xié)程使用了async啟動(dòng),哈哈,這就奇怪了,為什么會(huì)這樣?

3.向用戶暴露異常

還記得async啟動(dòng)的協(xié)程返回的是一個(gè)Deferred么,它可以使用await函數(shù),來獲取協(xié)程運(yùn)行結(jié)果。那么試想一下,如果我就是想要一個(gè)協(xié)程執(zhí)行完返回一個(gè)異常呢?

所以async中的異常會(huì)作為返回值,返回給調(diào)用await函數(shù)

fun `test coroutineScope exception4`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = CoroutineScope(Dispatchers.IO).async{
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
        println("new CoroutineScope finished")
    }
    job2.await()
    delay(3000)
    println("finished")
}

結(jié)果:

job2 finished
Exception in thread "main" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception4$1$job2$1.invokeSuspend(exceptionTest.kt:96)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)

Process finished with exit code 1

await的時(shí)候出現(xiàn)異常了,當(dāng)然會(huì)導(dǎo)致協(xié)程退出,我們可以在await的時(shí)候捕獲下這個(gè)異常,就不會(huì)影響主線程上下文的協(xié)程運(yùn)行了

fun `test coroutineScope exception4`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = CoroutineScope(Dispatchers.IO).async {
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
        println("new CoroutineScope finished")
    }
    try {
        job2.await()
    } catch (e: Exception) {
        e.printStackTrace()
    }
    delay(3000)
    println("finished")
}

結(jié)果:

job2 finished
java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception4$1$job2$1.invokeSuspend(exceptionTest.kt:96)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
job finished
finished

Process finished with exit code 0

值得注意的是,同一繼承關(guān)系下的協(xié)程使用await并無法捕獲異常,還是會(huì)遵循第一條,導(dǎo)致整個(gè)協(xié)程生命周期結(jié)束

fun `test coroutineScope exception5`() = runBlocking {
    val job2 = CoroutineScope(Dispatchers.IO).launch {
        val job1 = launch {
            delay(2000)
            println("job finished")
        }
        val job3 = async {
            delay(1000)
            println("job3 finished")
            throw IllegalArgumentException()
        }
        try {
            job3.await()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        delay(2000)
        println("job2 finished")
    }
    job2.join()
    println("finished")
}

結(jié)果:

job3 finished
java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception5$1$job2$1$job3$1.invokeSuspend(exceptionTest.kt:119)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
Exception in thread "DefaultDispatcher-worker-1" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception5$1$job2$1$job3$1.invokeSuspend(exceptionTest.kt:119)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
finished

Process finished with exit code 0

可以發(fā)現(xiàn)job3.await()的try catch并沒有生效,所以向用戶暴露異常只適用于不同上下文(沒有繼承關(guān)系)的協(xié)程

三、協(xié)程的異常處理

使用SupervisorJob

如果想要一個(gè)協(xié)程出現(xiàn)異常后,不影響其繼承關(guān)系中的其他協(xié)程,可以使用SupervisorJob

fun `test SupervisorJob exception`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = async(SupervisorJob()) {
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
    }
    delay(3000)
    println("finished")
}

結(jié)果:
job2 finished
job finished
finished

可以看到,job2的異常并沒有影響其他繼承關(guān)系的協(xié)程的執(zhí)行

SupervisorScope,這個(gè)我們前面已經(jīng)用過了,就不重復(fù)介紹了

異常捕獲器CoroutineExceptionHandler

協(xié)程上下文的4項(xiàng)之一,可以用CrashHandler理解,不過它并不能阻止協(xié)程的退出,只能夠獲取異常的信息

它使用有兩個(gè)條件:

1.異常是自動(dòng)拋出異常(launch)

2.實(shí)例化CoroutineScope的時(shí)候指定異常捕獲器 或者 在一個(gè)根協(xié)程中

例子1:

fun `test SupervisorHandler exception1`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(handler)
    val job1 = scope.launch {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        delay(2000)
        println("job finished")
    }
    delay(4000)
    println("finished")
}

結(jié)果:
job2 finished
caught: java.lang.IllegalArgumentException
finished

job2拋出了異常,被捕獲到了,但是scope的其他協(xié)程隨之生命周期也都結(jié)束了

例子2:

fun `test SupervisorHandler exception2`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch(handler) {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        delay(2000)
        println("job finished")
    }
    delay(4000)
    println("finished")
}

結(jié)果:
job2 finished
caught: java.lang.IllegalArgumentException
finished

和例子1相同,因?yàn)槲覀僪andler指定在了根協(xié)程

例子3:

fun `test SupervisorHandler exception3`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch {
        val job2 = launch(handler) {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        delay(2000)
        println("job finished")
    }
    delay(4000)
    println("finished")
}

結(jié)果:

job2 finished
Exception in thread "DefaultDispatcher-worker-4" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test SupervisorHandler exception$1$job1$1$job2$1.invokeSuspend(exceptionTest.kt:161)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
finished

Process finished with exit code 0

handler不是在根協(xié)程中,不能捕獲

如果一個(gè)子協(xié)程會(huì)拋出異常,那么對它進(jìn)行等待時(shí)(join或await),包裹一層try catch 會(huì)出現(xiàn)意料之外的事

例子4:

fun `test SupervisorHandler exception4`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch(handler) {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        try {
            job2.join()
        }catch (e:Exception){
        }
//        val job3 = scope.launch {
//            println("job3 finished")
//        }
        println("job delay")
        delay(2000)
        for(i in 0..10){
            println(i)
        }
        println("job finished")
    }
    delay(4000)
    println("finished")
}

結(jié)果:
job2 finished
job delay
caught: java.lang.IllegalArgumentException
finished

如果把scope根協(xié)程中的delay函數(shù)注釋掉,會(huì)怎么樣呢?

fun `test SupervisorHandler exception4`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch(handler) {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        try {
            job2.join()
        }catch (e:Exception){
        }
//        val job3 = scope.launch {
//            println("job3 finished")
//        }
        println("job delay")
//        delay(2000)
        for(i in 0..10){
            println(i)
        }
        println("job finished")
    }
    delay(4000)
    println("finished")
}

結(jié)果:
job2 finished
job delay
0
1
2
3
4
5
6
7
8
9
10
job finished
caught: java.lang.IllegalArgumentException

如果不包裹try catch 那么println("job delay")都不會(huì)執(zhí)行

由例子4和例子5,我們可以推斷,如果子協(xié)程有異常發(fā)生了,我們在等待時(shí)捕獲異常后,根協(xié)程執(zhí)行了掛起函數(shù),那么它會(huì)直接中斷,不執(zhí)行掛起函數(shù)以下的代碼,如果沒有掛起函數(shù),那么后面的代碼還是會(huì)執(zhí)行

為了加強(qiáng)驗(yàn)證這點(diǎn),我們使用Thread.sleep(2000)替換delay函數(shù)測試下:

fun `test SupervisorHandler exception4`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch(handler) {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        try {
            job2.join()
        }catch (e:Exception){
        }
//        val job3 = scope.launch {
//            println("job3 finished")
//        }
        println("job delay")
//        delay(2000)
        Thread.sleep(2000)
        for(i in 0..10){
            println(i)
        }
        println("job finished")
    }
    delay(4000)
    println("finished")
}

結(jié)果還是和例子5一樣:
job2 finished
job delay
0
1
2
3
4
5
6
7
8
9
10
job finished
caught: java.lang.IllegalArgumentException
finished

Process finished with exit code 0

其實(shí)出現(xiàn)這個(gè)情況,和我們之前取消協(xié)程是一樣的,出現(xiàn)異常后會(huì)開始取消協(xié)程,但是CPU密集型的代碼還會(huì)執(zhí)行,但是遇到掛起函數(shù)就會(huì)拋一個(gè)CancellationException,導(dǎo)致協(xié)程結(jié)束運(yùn)行,如果我們在掛起函數(shù)加上try catch打印,那么我們就可以看到CancellationException了

例子6,把job3的注釋放開:

fun `test SupervisorHandler exception4`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch(handler) {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        try {
            job2.join()
        }catch (e:Exception){
        }
        val job3 = scope.launch {
            println("job3 finished")
        }
        println("job delay")
        delay(2000)
//        Thread.sleep(2000)
        for(i in 0..10){
            println(i)
        }
        println("job finished")
    }
    delay(4000)
    println("finished")
}

結(jié)果:

job2 finished
job delay
caught: java.lang.IllegalArgumentException
Exception in thread "DefaultDispatcher-worker-1" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test SupervisorHandler exception4$1$job1$1$job2$1.invokeSuspend(exceptionTest.kt:227)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
finished

Process finished with exit code 0

顯然有異常沒有被捕獲,很明顯這個(gè)異常是調(diào)用job3時(shí)輸出的,由此又可以推斷出,如果在等待任務(wù)結(jié)束時(shí),任務(wù)出現(xiàn)異常并且手動(dòng)捕獲異常后,再啟動(dòng)子協(xié)程時(shí),也會(huì)拋出異常,并且不可捕獲
注意:新版本kotlin已修復(fù)這個(gè)bug,不會(huì)拋出異常了

Android中全局異常的處理

最后,感謝動(dòng)腦學(xué)院Jason老師出的kotlin協(xié)程教程,得到了很多理解和啟發(fā)

以上就是kotlin 協(xié)程上下文異常處理詳解的詳細(xì)內(nèi)容,更多關(guān)于kotlin 協(xié)程上下文異常處理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android SeekBar 自定義thumb旋轉(zhuǎn)動(dòng)畫效果

    Android SeekBar 自定義thumb旋轉(zhuǎn)動(dòng)畫效果

    某些音樂播放或者視頻播放的界面上,資源還在加載時(shí),進(jìn)度條的原點(diǎn)(thumb)會(huì)顯示一個(gè)轉(zhuǎn)圈的效果。這篇文章主要介紹了Android SeekBar 自定義thumb thumb旋轉(zhuǎn)動(dòng)畫效果,需要的朋友可以參考下
    2021-11-11
  • Android中Listview點(diǎn)擊item不變顏色及設(shè)置listselector 無效的解決方案

    Android中Listview點(diǎn)擊item不變顏色及設(shè)置listselector 無效的解決方案

    這篇文章主要介紹了Android中Listview點(diǎn)擊item不變顏色及設(shè)置listselector 無效的原因及解決方案,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-09-09
  • Android在Kotlin中更好地使用LitePal

    Android在Kotlin中更好地使用LitePal

    今天小編就為大家分享一篇關(guān)于Android在Kotlin中更好地使用LitePal,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • 搭建簡易藍(lán)牙定位系統(tǒng)的實(shí)現(xiàn)方法

    搭建簡易藍(lán)牙定位系統(tǒng)的實(shí)現(xiàn)方法

    下面小編就為大家?guī)硪黄罱ê喴姿{(lán)牙定位系統(tǒng)的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-03-03
  • Android開發(fā)自學(xué)筆記(一):Hello,world!

    Android開發(fā)自學(xué)筆記(一):Hello,world!

    這篇文章主要介紹了Android開發(fā)自學(xué)筆記(一):Hello,world!本文講解了創(chuàng)建HelloWorld工程、編寫代碼、啟動(dòng)模擬器等步驟,需要的朋友可以參考下
    2015-04-04
  • Android之OOM異常解決案例講解

    Android之OOM異常解決案例講解

    這篇文章主要介紹了Android之OOM異常解決案例講解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Android編程實(shí)現(xiàn)扭曲圖像的繪制功能示例

    Android編程實(shí)現(xiàn)扭曲圖像的繪制功能示例

    這篇文章主要介紹了Android編程實(shí)現(xiàn)扭曲圖像的繪制功能,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android圖形扭曲的具體操作步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-09-09
  • Android?Studio?2022.1.1創(chuàng)建項(xiàng)目的Gradle配置問題

    Android?Studio?2022.1.1創(chuàng)建項(xiàng)目的Gradle配置問題

    這篇文章主要介紹了Android?Studio?2022.1.1創(chuàng)建項(xiàng)目的Gradle配置問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • 在RecyclerView中實(shí)現(xiàn)button的跳轉(zhuǎn)功能

    在RecyclerView中實(shí)現(xiàn)button的跳轉(zhuǎn)功能

    本次實(shí)驗(yàn)就是在RecyclerView中添加一個(gè)button控件并實(shí)現(xiàn)監(jiān)聽,使鼠標(biāo)點(diǎn)擊時(shí)可以跳轉(zhuǎn)到另外一個(gè)設(shè)計(jì)好的界面,對RecyclerView實(shí)現(xiàn)button跳轉(zhuǎn)功能感興趣的朋友一起看看吧
    2021-10-10
  • Android ViewFlipper翻轉(zhuǎn)視圖使用詳解

    Android ViewFlipper翻轉(zhuǎn)視圖使用詳解

    這篇文章主要為大家詳細(xì)介紹了Android ViewFlipper翻轉(zhuǎn)視圖的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05

最新評論