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

詳解Kotlin協(xié)程的異常處理機(jī)制

 更新時(shí)間:2023年09月07日 11:17:11   作者:淘淘養(yǎng)樂多  
協(xié)程會(huì)遇到各種異常情況,比如協(xié)程被取消、協(xié)程內(nèi)部發(fā)生錯(cuò)誤、協(xié)程之間的異常傳播等,這些異常情況需要我們正確地處理,否則可能會(huì)導(dǎo)致程序崩潰、資源泄露或者邏輯錯(cuò)誤,本文將介紹 Kotlin 協(xié)程的異常處理機(jī)制,需要的朋友可以參考下

Kotlin 協(xié)程的異常處理

協(xié)程會(huì)遇到各種異常情況,比如協(xié)程被取消、協(xié)程內(nèi)部發(fā)生錯(cuò)誤、協(xié)程之間的異常傳播等。這些異常情況需要我們正確地處理,否則可能會(huì)導(dǎo)致程序崩潰、資源泄露或者邏輯錯(cuò)誤。本文將介紹 Kotlin 協(xié)程的異常處理機(jī)制,包括以下幾個(gè)方面:

  • 協(xié)程的取消:介紹了協(xié)程中如何處理不同類型的異常,包括取消異常其他異常,以及一些注意事項(xiàng)和技巧。
  • 協(xié)程的取消:需要協(xié)程內(nèi)部配合,可以使用isActive或者掛起函數(shù)來響應(yīng)外部的**cancel()**方法,不要打破結(jié)構(gòu)化的父子關(guān)系。
  • CancellationException異常:是一種特殊的異常,用于實(shí)現(xiàn)協(xié)程的結(jié)構(gòu)化取消,當(dāng)捕獲到這種異常時(shí),要考慮是否需要重新拋出。
  • 其他異常處理手段:介紹了幾種常用的方法,如try-catch、SupervisorJobCoroutineExceptionHandler,以及它們的使用場(chǎng)景和注意點(diǎn)。

協(xié)程的取消

協(xié)程的取消是一種協(xié)作機(jī)制,也就是說,協(xié)程需要主動(dòng)檢查自己是否被取消,并在合適的時(shí)候停止執(zhí)行。這樣做的好處是可以避免在不安全的狀態(tài)下終止協(xié)程,比如在操作共享資源或者執(zhí)行不可逆操作時(shí)。協(xié)程可以通過以下幾種方式來檢查自己是否被取消:

  • 使用 isActive 屬性:這是一個(gè)布爾值,表示當(dāng)前協(xié)程是否處于活動(dòng)狀態(tài)(即未被取消)。如果為 false,則說明協(xié)程已經(jīng)被取消或者完成了。我們可以在協(xié)程中定期檢查這個(gè)屬性,并在發(fā)現(xiàn)為 false 時(shí)退出循環(huán)或者返回結(jié)果。
  • 使用 掛起函數(shù):所有 kotlinx.coroutines 中的掛起函數(shù)都是 可被取消的 。它們檢查協(xié)程的取消,并在取消時(shí)拋出 CancellationException 異常。因此,我們可以在協(xié)程中調(diào)用任何掛起函數(shù)(比如 delay()、withTimeout() 等),來響應(yīng)外部的取消請(qǐng)求。如果不想處理這個(gè)異常,可以直接讓它拋出,協(xié)程會(huì)自動(dòng)結(jié)束。

下面是一個(gè)簡(jiǎn)單的例子,演示了如何使用 isActive 和 delay() 來實(shí)現(xiàn)可被取消的協(xié)程:

import kotlinx.coroutines.*
fun main() = runBlocking {
    // 創(chuàng)建一個(gè) Job 對(duì)象
    val job = launch {
        // 在一個(gè)循環(huán)中執(zhí)行一些工作
        var i = 0
        while (isActive) { // 檢查協(xié)程是否被取消
            println("job: I'm working...${i++}")
            // 模擬耗時(shí)操作
            delay(500L)
        }
    }
    // 等待一段時(shí)間
    delay(1300L)
    println("main: I'm tired of waiting!")
    // 取消協(xié)程
    job.cancel()
    println("main: Now I can quit.")
}

輸出結(jié)果:

job: I'm working...0
job: I'm working...1
job: I'm working...2
main: I'm tired of waiting!
main: Now I can quit.

從輸出結(jié)果可以看出,在調(diào)用 job.cancel() 后,循環(huán)就停止了,并沒有繼續(xù)打印 "job: I'm working..."。這是因?yàn)?delay() 函數(shù)在檢測(cè)到協(xié)程被取消時(shí),拋出了 CancellationException 異常,導(dǎo)致協(xié)程結(jié)束。如果我們不使用 delay(),而是使用 Thread.sleep(),那么協(xié)程就不會(huì)響應(yīng)取消,而是繼續(xù)執(zhí)行,直到循環(huán)結(jié)束。這是因?yàn)?Thread.sleep() 是一個(gè)阻塞函數(shù),它不會(huì)檢查協(xié)程的取消狀態(tài),也不會(huì)拋出任何異常。因此,我們應(yīng)該盡量避免在協(xié)程中使用阻塞函數(shù),而是使用掛起函數(shù)。

CancellationException 異常

CancellationException 是一種特殊的異常,它用于表示協(xié)程的正常取消。它繼承自 IllegalStateException,但是有以下幾個(gè)特點(diǎn):

  • 它不會(huì)打印堆棧信息,也不會(huì)被默認(rèn)的異常處理器捕獲,因?yàn)樗淮沓绦虻腻e(cuò)誤,而是協(xié)程的協(xié)作方式。
  • 它可以被 catch 語句捕獲,但是一般不需要這樣做,除非我們想在協(xié)程取消時(shí)執(zhí)行一些額外的操作,比如釋放資源、關(guān)閉連接等。如果我們只是想在協(xié)程取消時(shí)退出循環(huán)或者返回結(jié)果,那么不需要捕獲這個(gè)異常,直接讓它拋出即可。
  • 它可以被 throw 語句拋出,用于主動(dòng)取消協(xié)程。我們可以在協(xié)程中調(diào)用 cancel() 方法來取消自己或者父協(xié)程,也可以直接拋出 CancellationException 來達(dá)到同樣的效果。不過,前者更加優(yōu)雅和明確,后者更加靈活和隱晦。

下面是一個(gè)例子,演示了如何在協(xié)程取消時(shí)捕獲和拋出 CancellationException 異常:

import kotlinx.coroutines.*
fun main() = runBlocking {
    // 創(chuàng)建一個(gè) Job 對(duì)象
    val job = launch {
        try {
            // 在一個(gè)循環(huán)中執(zhí)行一些工作
            var i = 0
            while (isActive) { // 檢查協(xié)程是否被取消
                println("job: I'm working...${i++}")
                // 模擬耗時(shí)操作
                delay(500L)
            }
        } catch (e: CancellationException) {
            // 捕獲取消異常
            println("job: I'm cancelled, reason: ${e.message}")
        } finally {
            // 在 finally 塊中執(zhí)行一些操作
            println("job: I'm in the finally block")
            // 拋出取消異常
            throw CancellationException("I don't want to finish normally")
        }
    }
    // 等待一段時(shí)間
    delay(1300L)
    println("main: I'm tired of waiting!")
    // 取消協(xié)程,并傳遞一個(gè)原因
    job.cancel(CancellationException("Too slow"))
    println("main: Now I can quit.")
}

輸出結(jié)果:

job: I'm working...0
job: I'm working...1
job: I'm working...2
main: I'm tired of waiting!
job: I'm cancelled, reason: Too slow
job: I'm in the finally block
main: Now I can quit.

從輸出結(jié)果可以看出,在調(diào)用 job.cancel() 后,協(xié)程進(jìn)入了 catch 語句,并打印了取消的原因。然后進(jìn)入了 finally 塊,并打印了一條信息。最后,在 finally 塊中拋出了一個(gè)新的 CancellationException 異常,并傳遞了一個(gè)自定義的消息。這個(gè)異常并沒有被打印或者捕獲,而是被忽略了。這是因?yàn)楫?dāng)一個(gè)協(xié)程被取消時(shí),它只關(guān)心第一個(gè) CancellationException 異常,并以它作為結(jié)束的原因。后續(xù)的任何 CancellationException 異常都會(huì)被忽略。

其他異常處理手段

除了使用 try-catch 語句來處理協(xié)程中的異常外

  • 使用 SupervisorJob:這是一種特殊的 Job,它可以讓協(xié)程的子協(xié)程在發(fā)生異常時(shí)不影響父協(xié)程和其他兄弟協(xié)程的運(yùn)行。這樣,我們可以在父協(xié)程中創(chuàng)建多個(gè)子協(xié)程,分別執(zhí)行不同的任務(wù),而不用擔(dān)心其中一個(gè)任務(wù)失敗導(dǎo)致其他任務(wù)也被取消。這種方法適用于那些子協(xié)程之間沒有依賴關(guān)系,且不需要統(tǒng)一的異常處理邏輯的場(chǎng)景。
  • 使用 CoroutineExceptionHandler:這是一種 CoroutineContext 的元素,它可以定義一個(gè)函數(shù),用于處理協(xié)程中未捕獲的異常我們可以在創(chuàng)建協(xié)程時(shí),將這個(gè)元素添加到協(xié)程的上下文中,或者使用 coroutineScope 或者 supervisorScope 函數(shù)來創(chuàng)建一個(gè)新的作用域,并將這個(gè)元素添加到作用域的上下文中。這樣,當(dāng)作用域內(nèi)的任何協(xié)程發(fā)生未捕獲的異常時(shí),都會(huì)調(diào)用這個(gè)函數(shù)來處理。這種方法適用于那些需要統(tǒng)一的異常處理邏輯,或者需要在異常發(fā)生時(shí)執(zhí)行一些操作(比如日志、通知等)的場(chǎng)景。

下面是一個(gè)例子,演示了如何使用 SupervisorJob 和 CoroutineExceptionHandler 來處理協(xié)程中的異常:

import kotlinx.coroutines.*
fun main() = runBlocking {
    // 創(chuàng)建一個(gè) SupervisorJob 對(duì)象
    val supervisor = SupervisorJob()
    // 創(chuàng)建一個(gè) CoroutineExceptionHandler 對(duì)象
    val handler = CoroutineExceptionHandler { context, exception ->
        // 處理未捕獲的異常
        println("Caught $exception in ${context[CoroutineName]}")
    }
    // 使用 supervisor 和 handler 創(chuàng)建一個(gè)新的作用域
    supervisorScope {
        // 在作用域內(nèi)創(chuàng)建三個(gè)子協(xié)程
        val child1 = launch(supervisor + CoroutineName("child1")) {
            println("child1: I'm working...")
            delay(500L)
            println("child1: I'm done.")
        }
        val child2 = launch(supervisor + CoroutineName("child2")) {
            println("child2: I'm working...")
            delay(1000L)
            // 拋出一個(gè)異常
            throw ArithmeticException("Oops!")
        }
        val child3 = launch(supervisor + handler + CoroutineName("child3")) {
            println("child3: I'm working...")
            delay(1500L)
            println("child3: I'm done.")
        }
    }
    // 等待作用域結(jié)束
    println("main: The scope is over.")
}

輸出結(jié)果:

child1: I'm working...
child2: I'm working...
child3: I'm working...
child1: I'm done.
Caught java.lang.ArithmeticException: Oops! in child2
child3: I'm done.
main: The scope is over.

從輸出結(jié)果可以看出,在 child2 拋出異常后,并沒有影響 child1 和 child3 的運(yùn)行,它們都正常地完成了自己的任務(wù)。這是因?yàn)槭褂昧?SupervisorJob 來創(chuàng)建作用域,使得子協(xié)程之間互不影響。同時(shí),我們也可以看到,在 child2 拋出異常后,調(diào)用了 CoroutineExceptionHandler 來處理這個(gè)異常,并打印了相關(guān)信息。這是因?yàn)槭褂昧?handler 來定義一個(gè)統(tǒng)一的異常處理函數(shù),并將它添加到 child3 的上下文中。注意,handler 并沒有添加到 child2 的上下文中,因?yàn)槿绻@樣做,那么 child2 的異常就會(huì)被捕獲并處理,而不會(huì)傳播到父協(xié)程和其他兄弟協(xié)程中。這樣就會(huì)打破 SupervisorJob 的語義,使得父協(xié)程和其他兄弟協(xié)程無法感知到 child2 的異常。因此,在使用 SupervisorJob 時(shí),我們應(yīng)該避免在子協(xié)程中使用 CoroutineExceptionHandler,而是在父協(xié)程或者其他兄弟協(xié)程中使用。

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

相關(guān)文章

  • Android自定義View實(shí)現(xiàn)彈幕效果

    Android自定義View實(shí)現(xiàn)彈幕效果

    這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)彈幕效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • AndroidStudio不自動(dòng)添加新創(chuàng)建的文件到VCS的解決辦法

    AndroidStudio不自動(dòng)添加新創(chuàng)建的文件到VCS的解決辦法

    這篇文章主要介紹了AndroidStudio不自動(dòng)添加新創(chuàng)建的文件到VCS的解決辦法的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Android中Activity過渡動(dòng)畫的實(shí)例講解

    Android中Activity過渡動(dòng)畫的實(shí)例講解

    在android5.0 以上版本中,google為我們提供了幾種activity切換的過渡動(dòng)畫,目的是為了讓 activity 切換轉(zhuǎn)場(chǎng)更加美觀,下面這篇文章主要給大家介紹了關(guān)于Android中Activity過渡動(dòng)畫的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • Android注解使用之ButterKnife 8.0詳解

    Android注解使用之ButterKnife 8.0詳解

    這篇文章主要為大家詳細(xì)介紹了Android注解使用之ButterKnife 8.0的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Android DataBinding的官方雙向綁定示例

    Android DataBinding的官方雙向綁定示例

    本篇文章主要介紹了Android DataBinding的官方雙向綁定示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • Android自定義控件單位尺寸實(shí)現(xiàn)代碼

    Android自定義控件單位尺寸實(shí)現(xiàn)代碼

    這篇文章主要介紹了Android自定義控件單位尺寸實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • OpenHarmony如何調(diào)用電話服務(wù)API撥打電話

    OpenHarmony如何調(diào)用電話服務(wù)API撥打電話

    OpenHarmony3.1版本標(biāo)準(zhǔn)系統(tǒng)增加了通話相關(guān)的聯(lián)系人應(yīng)用,來電應(yīng)用等,在系統(tǒng)服務(wù)層面電話相關(guān)功能也比較完善,這篇文章主要介紹了OpenHarmony如何調(diào)用電話服務(wù)API撥打電話
    2022-11-11
  • ViewPager實(shí)現(xiàn)圖片切換效果

    ViewPager實(shí)現(xiàn)圖片切換效果

    這篇文章主要為大家詳細(xì)介紹了ViewPager實(shí)現(xiàn)圖片切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Android Bitmap的加載優(yōu)化與Cache相關(guān)介紹

    Android Bitmap的加載優(yōu)化與Cache相關(guān)介紹

    這篇文章主要介紹了Android中性能優(yōu)化之Bitmap的加載優(yōu)化與Cache相關(guān)內(nèi)容介紹,文中介紹的很詳細(xì),對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。
    2017-02-02
  • Android仿支付寶支付密碼輸入框

    Android仿支付寶支付密碼輸入框

    這篇文章主要為大家詳細(xì)介紹了Android仿支付寶支付密碼輸入框的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11

最新評(píng)論