国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

[Kotlin Tutorials 22] 協(xié)程中的異常處理

這篇具有很好參考價(jià)值的文章主要介紹了[Kotlin Tutorials 22] 協(xié)程中的異常處理。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

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

[Kotlin Tutorials 22] 協(xié)程中的異常處理

Parent-Child關(guān)系

如果一個(gè)coroutine拋出了異常, 它將會(huì)把這個(gè)exception向上拋給它的parent, 它的parent會(huì)做以下三件事情:

  • 取消其他所有的children.
  • 取消自己.
  • 把exception繼續(xù)向上傳遞.

這是默認(rèn)的異常處理關(guān)系, 取消是雙向的, child會(huì)取消parent, parent會(huì)取消所有child.

catch不住的exception

看這個(gè)代碼片段:

fun main() {
    val scope = CoroutineScope(Job())
    try {
        scope.launch {
            throw RuntimeException()
        }
    } catch (e: Exception) {
        println("Caught: $e")
    }

    Thread.sleep(100)
}

這里的異常catch不住了.
會(huì)直接讓main函數(shù)的主進(jìn)程崩掉.

這是因?yàn)楹推胀ǖ漠惓L幚頇C(jī)制不同, coroutine中未被處理的異常并不是直接拋出, 而是按照job hierarchy向上傳遞給parent.

如果把try放在launch里面還行.

默認(rèn)的異常處理

默認(rèn)情況下, child發(fā)生異常, parent和其他child也會(huì)被取消.

fun main() {
    println("start")
    val exceptionHandler = CoroutineExceptionHandler { _, exception ->
        println("CoroutineExceptionHandler got $exception")
    }
    val scope = CoroutineScope(Job() + exceptionHandler)

    scope.launch {
        println("child 1")
        delay(1000)
        println("finish child 1")
    }.invokeOnCompletion { throwable ->
        if (throwable is CancellationException) {
            println("Coroutine 1 got cancelled!")
        }
    }

    scope.launch {
        println("child 2")
        delay(100)
        println("child 2 throws exception")
        throw RuntimeException()
    }

    Thread.sleep(2000)
    println("end")
}

打印出:

start
child 1
child 2
child 2 throws exception
Coroutine 1 got cancelled!
CoroutineExceptionHandler got java.lang.RuntimeException
end

SupervisorJob

如果有一些情形, 開啟了多個(gè)child job, 但是卻不想因?yàn)槠渲幸粋€(gè)的失敗而取消其他, 怎么辦? 用SupervisorJob.

比如:

val uiScope = CoroutineScope(SupervisorJob())

如果你用的是scope builder, 那么用supervisorScope.

SupervisorJob改造上面的例子:

fun main() {
    println("start")
    val exceptionHandler = CoroutineExceptionHandler { _, exception ->
        println("CoroutineExceptionHandler got $exception")
    }
    val scope = CoroutineScope(SupervisorJob() + exceptionHandler)

    scope.launch {
        println("child 1")
        delay(1000)
        println("finish child 1")
    }.invokeOnCompletion { throwable ->
        if (throwable is CancellationException) {
            println("Coroutine 1 got cancelled!")
        }
    }

    scope.launch {
        println("child 2")
        delay(100)
        println("child 2 throws exception")
        throw RuntimeException()
    }
    Thread.sleep(2000)
    println("end")
}

輸出:

start
child 1
child 2
child 2 throws exception
CoroutineExceptionHandler got java.lang.RuntimeException
finish child 1
end

盡管coroutine 2拋出了異常, 另一個(gè)coroutine還是做完了自己的工作.

SupervisorJob的特點(diǎn)

SupervisorJob把取消變成了單向的, 只能從上到下傳遞, 只能parent取消child, 反之不能取消.
這樣既顧及到了由于生命周期的結(jié)束而需要的正常取消, 又避免了由于單個(gè)的child失敗而取消所有.

viewModelScope的context就是用了SupervisorJob() + Dispatchers.Main.immediate.

除了把取消變?yōu)閱蜗虻? supervisorScope也會(huì)和coroutineScope一樣等待所有child執(zhí)行結(jié)束.

supervisorScope中直接啟動(dòng)的coroutine是頂級(jí)coroutine.
頂級(jí)coroutine的特性:

  • 可以加exception handler.
  • 自己處理exception.
    比如上面的例子中coroutine child 2可以直接加exception handler.

使用注意事項(xiàng), SupervisorJob只有兩種寫法:

  • 作為CoroutineScope的參數(shù)傳入: CoroutineScope(SupervisorJob()).
  • 使用supervisorScope方法.

把Job作為coroutine builder(比如launch)的參數(shù)傳入是錯(cuò)誤的做法, 不起作用, 因?yàn)橐粋€(gè)新的coroutine總會(huì)assign一個(gè)新的Job.

異常處理的辦法

try-catch

和普通的異常處理一樣, 我們可以用try-catch, 只是注意要在coroutine里面:

fun main() {
    val scope = CoroutineScope(Job())
    scope.launch {
        try {
            throw RuntimeException()
        } catch (e: Exception) {
            println("Caught: $e")
        }
    }

    Thread.sleep(100)
}

這樣就能打印出:

Caught: java.lang.RuntimeException

對(duì)于launch, try要包住整塊.
對(duì)于async, try要包住await語(yǔ)句.

scope function: coroutineScope()

coroutineScope會(huì)把其中未處理的exception拋出來(lái).

相比較于這段代碼中catch不到的exception:

fun main() {
    val scope = CoroutineScope(Job())
    scope.launch {
        try {
            launch {
                throw RuntimeException()
            }
        } catch (e: Exception) {
            println("Caught: $e")
        }
    }
    Thread.sleep(100)
}

沒(méi)走到catch里, 仍然是主進(jìn)程崩潰.

這個(gè)exception是可以catch到的:

fun main() {
    val scope = CoroutineScope(Job())
    scope.launch {
        try {
            coroutineScope {
                launch {
                    throw RuntimeException()
                }
            }
        } catch (e: Exception) {
            println("Caught: $e")
        }
    }

    Thread.sleep(100)
}

打印出:

Caught: java.lang.RuntimeException

因?yàn)檫@里coroutineScope把異常又重新拋出來(lái)了.

注意這里換成supervisorScope可是不行的.

CoroutineExceptionHandler

CoroutineExceptionHandler是異常處理的最后一個(gè)機(jī)制, 此時(shí)coroutine已經(jīng)結(jié)束了, 在這里的處理通常是報(bào)告log, 展示錯(cuò)誤等.
如果不加exception handler那么unhandled exception會(huì)進(jìn)一步往外拋, 如果最后都沒(méi)人處理, 那么可能造成進(jìn)程崩潰.

CoroutineExceptionHandler需要加在root coroutine上.

這是因?yàn)閏hild coroutines會(huì)把異常處理代理到它們的parent, 后者繼續(xù)代理到自己的parent, 一直到root.
所以對(duì)于非root的coroutine來(lái)說(shuō), 即便指定了CoroutineExceptionHandler也沒(méi)有用, 因?yàn)楫惓2粫?huì)傳到它.

兩個(gè)例外:

  • async的異常在Deferred對(duì)象中, CoroutineExceptionHandler也沒(méi)有任何作用.
  • supervision scope下的coroutine不會(huì)向上傳遞exception, 所以CoroutineExceptionHandler不用加在root上, 每個(gè)coroutine都可以加, 單獨(dú)處理.

通過(guò)這個(gè)例子可以看出另一個(gè)特性: CoroutineExceptionHandler只有當(dāng)所有child都結(jié)束之后才會(huì)處理異常信息.

@OptIn(DelicateCoroutinesApi::class)
fun main() = runBlocking {
    val handler = CoroutineExceptionHandler { _, exception -> 
        println("CoroutineExceptionHandler got $exception") 
    }
    val job = GlobalScope.launch(handler) {
        launch { // the first child
            try {
                delay(Long.MAX_VALUE)
            } finally {
                withContext(NonCancellable) {
                    println("Children are cancelled, but exception is not handled until all children terminate")
                    delay(100)
                    println("The first child finished its non cancellable block")
                }
            }
        }
        launch { // the second child
            delay(10)
            println("Second child throws an exception")
            throw ArithmeticException()
        }
    }
    job.join()
}

輸出:

Second child throws an exception
Children are cancelled, but exception is not handled until all children terminate
The first child finished its non cancellable block
CoroutineExceptionHandler got java.lang.ArithmeticException

如果多個(gè)child都拋出異常, 只有第一個(gè)被handler處理, 其他都在exception.suppressed字段里.

fun main() = runBlocking {
    val handler = CoroutineExceptionHandler { _, exception ->
        println("CoroutineExceptionHandler got $exception with suppressed ${exception.suppressed.contentToString()}")
    }
    val job = GlobalScope.launch(handler) {
        launch {
            try {
                delay(Long.MAX_VALUE) // it gets cancelled when another sibling fails with IOException
            } finally {
                throw ArithmeticException() // the second exception
            }
        }
        launch {
            delay(100)
            throw IOException() // the first exception
        }
        delay(Long.MAX_VALUE)
    }
    job.join()
}

輸出:

CoroutineExceptionHandler got java.io.IOException with suppressed [java.lang.ArithmeticException]

單獨(dú)說(shuō)一下async

async比較特殊:

  • 作為top coroutine時(shí), 在await的時(shí)候try-catch異常.
  • 如果是非top coroutine, async塊里的異常會(huì)被立即拋出.

例子:

fun main() {
    val scope = CoroutineScope(SupervisorJob())
    val deferred = scope.async {
        throw RuntimeException("RuntimeException in async coroutine")
    }

    scope.launch {
        try {
            deferred.await()
        } catch (e: Exception) {
            println("Caught: $e")
        }
    }

    Thread.sleep(100)
}

這里由于用了SupervisorJob, 所以async是top coroutine.

fun main() {

    val coroutineExceptionHandler = CoroutineExceptionHandler { coroutineContext, exception ->
        println("Handle $exception in CoroutineExceptionHandler")
    }

    val topLevelScope = CoroutineScope(SupervisorJob() + coroutineExceptionHandler)
    topLevelScope.launch {
        async {
            throw RuntimeException("RuntimeException in async coroutine")
        }
    }
    Thread.sleep(100)
}

當(dāng)它不是top coroutine時(shí), 異常會(huì)被直接拋出.

特殊的CancellationException

CancellationException是特殊的exception, 會(huì)被異常處理機(jī)制忽略, 即便拋出也不會(huì)向上傳遞, 所以不會(huì)取消它的parent.
但是CancellationException不能被catch, 如果它不被拋出, 其實(shí)協(xié)程沒(méi)有被成功cancel, 還會(huì)繼續(xù)執(zhí)行.

CancellationException的透明特性:
如果CancellationException是由內(nèi)部的其他異常引起的, 它會(huì)向上傳遞, 并且把原始的那個(gè)異常傳遞上去.

@OptIn(DelicateCoroutinesApi::class)
fun main() = runBlocking {
    val handler = CoroutineExceptionHandler { _, exception ->
        println("CoroutineExceptionHandler got $exception")
    }
    val job = GlobalScope.launch(handler) {
        val inner = launch { // all this stack of coroutines will get cancelled
            launch {
                launch {
                    throw IOException() // the original exception
                }
            }
        }
        try {
            inner.join()
        } catch (e: CancellationException) {
            println("Rethrowing CancellationException with original cause")
            throw e // cancellation exception is rethrown, yet the original IOException gets to the handler
        }
    }
    job.join()
}

輸出:

Rethrowing CancellationException with original cause
CoroutineExceptionHandler got java.io.IOException

這里Handler拿到的是最原始的IOException.

Further Reading

官方文檔:

  • Coroutine exceptions handling

Android官方文檔上鏈接的博客和視頻:

  • Exceptions in coroutines
  • KotlinConf 2019: Coroutines! Gotta catch 'em all! by Florina Muntenescu & Manuel Vivo

其他:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-475343.html

  • Kotlin Coroutines and Flow - Use Cases on Android
  • Why exception handling with Kotlin Coroutines is so hard and how to successfully master it!

到了這里,關(guān)于[Kotlin Tutorials 22] 協(xié)程中的異常處理的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Kotlin Android中錯(cuò)誤及異常處理最佳實(shí)踐

    Kotlin Android中錯(cuò)誤及異常處理最佳實(shí)踐

    Kotlin在Android開發(fā)中的錯(cuò)誤處理機(jī)制以及其優(yōu)勢(shì) Kotlin具有強(qiáng)大的錯(cuò)誤處理功能 :Kotlin提供了強(qiáng)大的錯(cuò)誤處理功能,使處理錯(cuò)誤變得簡(jiǎn)潔而直接。這個(gè)特性幫助開發(fā)人員快速識(shí)別和解決錯(cuò)誤,減少了調(diào)試代碼所需的時(shí)間。 Kotlin的錯(cuò)誤處理特性 :Kotlin具有一些錯(cuò)誤處理特性,如

    2024年02月07日
    瀏覽(32)
  • 【kotlin 協(xié)程】萬(wàn)字協(xié)程 一篇完成kotlin 協(xié)程進(jìn)階

    【kotlin 協(xié)程】萬(wàn)字協(xié)程 一篇完成kotlin 協(xié)程進(jìn)階

    Kotlin 中的協(xié)程提供了一種全新處理并發(fā)的方式,可以在 Android 平臺(tái)上使用它來(lái)簡(jiǎn)化異步執(zhí)行的代碼。協(xié)程是從 Kotlin 1.3 版本開始引入,但這一概念在編程世界誕生的黎明之際就有了,最早使用協(xié)程的編程語(yǔ)言可以追溯到 1967 年的 Simula 語(yǔ)言。 在過(guò)去幾年間,協(xié)程這個(gè)概念發(fā)展

    2024年02月07日
    瀏覽(37)
  • FastAPI學(xué)習(xí)-22.response 異常處理 HTTPException

    某些情況下,需要向客戶端返回錯(cuò)誤提示。 這里所謂的客戶端包括前端瀏覽器、其他應(yīng)用程序、物聯(lián)網(wǎng)設(shè)備等。 需要向客戶端返回錯(cuò)誤提示的場(chǎng)景主要如下: 客戶端沒(méi)有執(zhí)行操作的權(quán)限 客戶端沒(méi)有訪問(wèn)資源的權(quán)限 客戶端要訪問(wèn)的項(xiàng)目不存在 等等 … 遇到這些情況時(shí),通常

    2024年02月07日
    瀏覽(12)
  • Unity 中的 async/await:優(yōu)雅處理異步任務(wù)與協(xié)程

    內(nèi)容將會(huì)持續(xù)更新,有錯(cuò)誤的地方歡迎指正,謝謝! ? Unity 中的 async/await:優(yōu)雅處理異步任務(wù)與協(xié)程Coroutine ? ? ? TechX 堅(jiān)持將創(chuàng)新的科技帶給世界! 擁有更好的學(xué)習(xí)體驗(yàn) —— 不斷努力,不斷進(jìn)步,不斷探索 TechX —— 心探索、心進(jìn)?。?助力快速掌握 async/await 異步等待 為初

    2024年02月06日
    瀏覽(31)
  • Kotlin 協(xié)程一 —— 協(xié)程 Coroutine

    1.1.1基本定義 進(jìn)程 進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序在一個(gè)數(shù)據(jù)集上的一次動(dòng)態(tài)執(zhí)行的過(guò)程,是操作系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位,是應(yīng)用程序運(yùn)行的載體。 進(jìn)程是資源分配的最小單位,在單核CPU中,同一時(shí)刻只有一個(gè)程序在內(nèi)存中被CPU調(diào)用運(yùn)行。 線程 基本的

    2024年02月05日
    瀏覽(20)
  • kotlin語(yǔ)法進(jìn)階 - 協(xié)程(一)協(xié)程基礎(chǔ)

    kotlin語(yǔ)法進(jìn)階 - 協(xié)程(一)協(xié)程基礎(chǔ)

    協(xié)程并不是一個(gè)新的概念,而是一個(gè)非常老的概念,很多語(yǔ)言都支持協(xié)程,建議去瀏覽器去了解一下協(xié)程的歷史和基本概念,這里我們只講一下kotlin中的協(xié)程的作用。 從代碼實(shí)現(xiàn)角度來(lái)看:kotlin協(xié)程底層是用線程實(shí)現(xiàn)的,是一個(gè)封裝完善供開發(fā)者使用的線程框架。kotlin的一個(gè)

    2024年02月09日
    瀏覽(20)
  • Kotlin協(xié)程-從一到多

    上一篇文章,我介紹了Kotlin協(xié)程的創(chuàng)建,使用,協(xié)作等內(nèi)容。本篇將引入更多的使用場(chǎng)景,繼續(xù)帶你走進(jìn)協(xié)程世界。 常用編程語(yǔ)言都會(huì)內(nèi)置對(duì)同一類型不同對(duì)象的數(shù)據(jù)集表示,我們通常稱之為容器類。不同的容器類適用于不同的使用場(chǎng)景。Kotlin的 Flow 就是在異步計(jì)算的需求下

    2024年02月09日
    瀏覽(19)
  • Android Kotlin 協(xié)程初探

    維基百科:協(xié)程,英文Coroutine [k?ru’tin] (可入廳),是計(jì)算機(jī)程序的一類組件,推廣了協(xié)作式多任務(wù)的子程序,允許執(zhí)行被掛起與被恢復(fù)。 作為Google欽定的Android開發(fā)首選語(yǔ)言Kotlin,協(xié)程并不是 Kotlin 提出來(lái)的新概念,目前有協(xié)程概念的編程語(yǔ)言有Lua語(yǔ)言、Python語(yǔ)言、Go語(yǔ)言

    2024年02月08日
    瀏覽(27)
  • Kotlin協(xié)程-從理論到實(shí)戰(zhàn)

    上一篇文章從理論上對(duì)Kotlin協(xié)程進(jìn)行了部分說(shuō)明,本文將在上一篇的基礎(chǔ)上,從實(shí)戰(zhàn)出發(fā),繼續(xù)協(xié)程之旅。 在Kotlin中,要想使用協(xié)程,首先需要使用協(xié)程創(chuàng)建器創(chuàng)建,但還有個(gè)前提——協(xié)程作用域( CoroutineScope )。在早期的Kotlin實(shí)現(xiàn)中,協(xié)程創(chuàng)建器是一等函數(shù),也就是說(shuō)我們隨

    2024年02月09日
    瀏覽(21)
  • Kotlin協(xié)程學(xué)習(xí)之-02

    協(xié)程的基本使用 GlobalScope.launch 生命周期與進(jìn)程一致,且無(wú)法取消 runBlocking 會(huì)阻塞線程,一般在測(cè)試階段可以使用 val coroutineScope = CoroutineScope(context) coroutineScope.launch 通過(guò)context參數(shù)去管理和控制協(xié)程的生命周期 用法 val coroutineScope = CoroutineScope(context) coroutineScope.launch(Dispatche

    2024年01月22日
    瀏覽(19)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包