最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

引言

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

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

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

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

代碼驗(yàn)證下:

fun `test context life`() = runBlocking {
    //定義一個作用域
    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é)果也會相應(yīng)的變?yōu)椋?/p>

323

通過上面的列子,來對協(xié)程上下文的有一個初步概念:可以說協(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)算符重載,我們可以用+號來組合一個CoroutineContext的元素

2.CorountineScope

一般情況下,協(xié)程體內(nèi)所有的子協(xié)程,都繼承至根協(xié)程,協(xié)程的繼承的關(guān)系不是我們所了解的類的繼承關(guān)系,而是父協(xié)程和子協(xié)程的生命周期關(guān)系,還記得我們上面舉得例子么,除非在協(xié)程體內(nèi)自己手動創(chuàng)建協(xié)程作用域,即:創(chuàng)建一個全新的協(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é)程,會阻塞主線程,實(shí)際上用的是一個EmptyCoroutineContext作為上下文,它是一個主線程的協(xié)程上下文,靜態(tài)的全局變量,我們其實(shí)就可以理解成是主線程
2.GlobalScope:也是用的EmptyCoroutineContext
3.MainScope:使用ContextScope構(gòu)造了新的上下文
4.coroutineScope:繼承的父協(xié)程上下文,不能算是全新的協(xié)程
等等

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

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

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

例子1:

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

結(jié)果:
main finished

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

例子2:

fun `test context life2`() = runBlocking {
    //定義一個作用域
    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é)程)中,手動調(diào)用jobA的join方法,那么主線程就會阻塞,直到j(luò)obA執(zhí)行完畢。這個和我們的多線程操作是一樣的,主線程等待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é)程名,也不會影響協(xié)程上下文繼承關(guān)系,主協(xié)程還是會等待子協(xié)程執(zhí)行完畢后才結(jié)束生命

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

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

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

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

例子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é)程異常會怎么樣?

例子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é)程之間,異常并不會自動傳遞,我們的主線程上下文協(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啟動,哈哈,這就奇怪了,為什么會這樣?

3.向用戶暴露異常

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

所以async中的異常會作為返回值,返回給調(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的時候出現(xiàn)異常了,當(dāng)然會導(dǎo)致協(xié)程退出,我們可以在await的時候捕獲下這個異常,就不會影響主線程上下文的協(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并無法捕獲異常,還是會遵循第一條,導(dǎo)致整個協(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

如果想要一個協(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,這個我們前面已經(jīng)用過了,就不重復(fù)介紹了

異常捕獲器CoroutineExceptionHandler

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

它使用有兩個條件:

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

2.實(shí)例化CoroutineScope的時候指定異常捕獲器 或者 在一個根協(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é)程中,不能捕獲

如果一個子協(xié)程會拋出異常,那么對它進(jìn)行等待時(join或await),包裹一層try catch 會出現(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ù)注釋掉,會怎么樣呢?

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")都不會執(zhí)行

由例子4和例子5,我們可以推斷,如果子協(xié)程有異常發(fā)生了,我們在等待時捕獲異常后,根協(xié)程執(zhí)行了掛起函數(shù),那么它會直接中斷,不執(zhí)行掛起函數(shù)以下的代碼,如果沒有掛起函數(shù),那么后面的代碼還是會執(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)這個情況,和我們之前取消協(xié)程是一樣的,出現(xiàn)異常后會開始取消協(xié)程,但是CPU密集型的代碼還會執(zhí)行,但是遇到掛起函數(shù)就會拋一個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

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

Android中全局異常的處理

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

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

相關(guān)文章

  • Android編程獲取包名,版本信息及VersionName名稱的方法

    Android編程獲取包名,版本信息及VersionName名稱的方法

    這篇文章主要介紹了Android編程獲取包名,版本信息及VersionName名稱的方法,涉及Android包及版本相關(guān)操作函數(shù)使用技巧,需要的朋友可以參考下
    2016-10-10
  • Android 路徑查詢具體實(shí)現(xiàn)

    Android 路徑查詢具體實(shí)現(xiàn)

    可以通過RasterMap的getDirection()方法來查詢路徑,和查詢地址類似,路徑查詢的結(jié)果也是通過回調(diào)函數(shù)的方式來通知應(yīng)用程序的,下面的例子返回南京到北京的路徑
    2013-10-10
  • Android實(shí)現(xiàn)一個帶粘連效果的LoadingBar

    Android實(shí)現(xiàn)一個帶粘連效果的LoadingBar

    Loading效果相信大家應(yīng)該都實(shí)現(xiàn)過,最近發(fā)現(xiàn)了一個不錯的效果,決定分享給大家,所以下面這篇文章主要給大家介紹了關(guān)于利用Android實(shí)現(xiàn)一個帶粘連效果的LoadingBar的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-12-12
  • Android實(shí)現(xiàn)定制返回按鈕動畫效果的方法

    Android實(shí)現(xiàn)定制返回按鈕動畫效果的方法

    這篇文章主要介紹了Android實(shí)現(xiàn)定制返回按鈕動畫效果的方法,涉及Android控件及動畫的相關(guān)操作技巧,需要的朋友可以參考下
    2016-02-02
  • Android編程之判斷SD卡狀態(tài)的方法

    Android編程之判斷SD卡狀態(tài)的方法

    這篇文章主要介紹了Android編程之判斷SD卡狀態(tài)的方法,結(jié)合實(shí)例分析了Android針對SD卡的權(quán)限操作及狀態(tài)判定技巧,需要的朋友可以參考下
    2016-02-02
  • Android?AMS啟動App進(jìn)程原理分析

    Android?AMS啟動App進(jìn)程原理分析

    這篇文章主要介紹了Android?AMS啟動App進(jìn)程原理,系統(tǒng)fork函數(shù)是如何創(chuàng)建進(jìn)程,文中有詳細(xì)的代碼示例,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-05-05
  • Monkeyrunner 常用按鍵總結(jié)

    Monkeyrunner 常用按鍵總結(jié)

    這篇文章主要介紹了Monkeyrunner 常用按鍵總結(jié)的相關(guān)資料,這里對Monkeyrunner 按鍵的功能進(jìn)行詳細(xì)說明,需要的朋友可以參考下
    2016-11-11
  • Android入門教程之組件Activity的生命周期詳解

    Android入門教程之組件Activity的生命周期詳解

    Activity作為四大組件之一,出現(xiàn)的頻率相當(dāng)高,基本上我們在android的各個地方都能看見它的蹤影,因此深入了解Activity,對于開發(fā)高質(zhì)量應(yīng)用程序是很有幫助的。今天我們就來詳細(xì)地聊聊Activity的生命周期,以便我們在以后的開發(fā)中能如魚得水
    2021-10-10
  • Android EventBus(普通事件/粘性事件)詳解

    Android EventBus(普通事件/粘性事件)詳解

    這篇文章主要為大家詳細(xì)介紹了Android EventBus 普通事件/粘性事件的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Android startActivityForResult()代替方案示例

    Android startActivityForResult()代替方案示例

    這篇文章主要為大家介紹了Android startActivityForResult()代替方案示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08

最新評論

鄂托克前旗| 麟游县| 郴州市| 娱乐| 明溪县| 祁连县| 舒兰市| 张北县| 南通市| 澄城县| 乌什县| 蕉岭县| 会昌县| 杭州市| 吕梁市| 宜城市| 安溪县| 上杭县| 黑龙江省| 澳门| 肇源县| 澄城县| 荥阳市| 舞阳县| 鸡泽县| 岐山县| 广河县| 德江县| 兴文县| 锡林浩特市| 博罗县| 和平县| 鄄城县| 泗洪县| 正安县| 衡东县| 咸宁市| 贺兰县| 丹巴县| 余庆县| 淄博市|