Kotlin Academy · Oppitunti

CoroutineExceptionHandler: maailmanlaajuinen käsittelemätön poikkeusten käsittelijä

Asentakaa CoroutineExceptionHandler kirjaamaan tai palauttamaan käsittelemättömistä poikkeuksista.

Oppitunti 2/412 vaihetta

CoroutineExceptionHandler: maailmanlaajuinen käsittelemätön poikkeusten käsittelijä on ilmainen Kotlin Academy-oppitunti CoddyKitissä. Tämä on oppitunti 2/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Kotlin Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Kotlin Academy-kurssilla on yhteensä 4 oppituntia.

Mikä on CoroutineExceptionHandler?

CoroutineExceptionHandler on kontekstielementti, joka käsittelee kiinniottamattomat poikkeukset korutiineista, joissa ei ole catch-käsittelijää. Se toimii viimeisenä keinona.

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { context, exception ->
    println("Caught unhandled: ${exception.message}")
}
fun main() = runBlocking {
    val scope = CoroutineScope(Dispatchers.Default + handler)
    scope.launch { throw RuntimeException("oops") }
    delay(100)
}

Vain juurikorutiineille

CoroutineExceptionHandler ottaa kiinni poikkeuksia vain root-korutiineista, jotka käynnistetään suoraan scopessa. Lapsikorutiinit välittävät poikkeukset vanhemmalleen, eivät käsittelijälle.

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e -> println("Handler: ${e.message}") }
fun main() = runBlocking {
    // Root coroutine — handler fires:
    CoroutineScope(handler).launch {
        throw RuntimeException("root error")
    }
    delay(100)
    // NOT handler (child of coroutineScope):
    // launch { launch { throw RuntimeException() } }
}

Poikkeusta ei vaimenneta

Käsittelijä kutsutaan sen jälkeen, kun poikkeus on jo peruuttanut korutiinin. Sitä käytetään lokitukseen, kaatumisten raportointiin tai siivoukseen — ei suorituksen jatkamiseen.

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e ->
    println("[CrashReport] ${e::class.simpleName}: ${e.message}")
    // send to Crashlytics, Sentry, etc.
}
fun main() = runBlocking {
    CoroutineScope(SupervisorJob() + handler).apply {
        launch { throw IllegalStateException("state error") }
        launch { delay(100); println("still alive") }
        delay(200)
        cancel()
    }
}

Käsittelijä + SupervisorJob

SupervisorJob + CoroutineExceptionHandler -yhdistelmä on pitkäkestoisten scopejen vakiomalli: lapset epäonnistuvat toisistaan riippumatta ja käsittelemättömät epäonnistumiset kirjataan lokiin.

import kotlinx.coroutines.*
class AppScope {
    private val handler = CoroutineExceptionHandler { _, e ->
        println("Uncaught: ${e.message}")
    }
    val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default + handler)
}
fun main() = runBlocking {
    val app = AppScope()
    app.scope.launch { throw RuntimeException("task failed") }
    app.scope.launch { delay(100); println("other task ok") }
    delay(200)
    app.scope.cancel()
}

Käsittelijä async-operaatioissa

async-operaatioissa poikkeukset tallennetaan Deferred-objektiin ja heitetään await()-kutsun yhteydessä. Käsittelijä EI käynnisty async-operaatioille, ellei deferrediä jätetä odottamatta.

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e -> println("Handler: ${e.message}") }
fun main() = runBlocking {
    val scope = CoroutineScope(SupervisorJob() + handler)
    val deferred = scope.async { throw RuntimeException("async error") }
    try {
        deferred.await()  // exception thrown here
    } catch (e: RuntimeException) {
        println("Caught from await: ${e.message}")
    }
    delay(50)
    scope.cancel()
}

Thread.UncaughtExceptionHandler-vertailu

Toisin kuin Javan UncaughtExceptionHandler, Kotlinin käsittelijä on osa korutiinin kontekstia ja koskee vain kyseisen scopen korutiineja.

import kotlinx.coroutines.*
// Java style (applies to threads):
Thread.setDefaultUncaughtExceptionHandler { t, e ->
    println("Thread ${t.name} threw: ${e.message}")
}
// Kotlin coroutine style (applies to coroutines in scope):
val handler = CoroutineExceptionHandler { _, e ->
    println("Coroutine threw: ${e.message}")
}
fun main() = runBlocking { println("Handlers target different concurrency models") }

Lokitusmalli MDC:n kanssa

Palvelinsovelluksissa käsittelijä voi kerätä korutiinin kontekstitietoja, kuten pyyntöjen tunnisteita, rakenteista lokitusta varten ennen tietojen välittämistä lokituskehykselle.

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { ctx, e ->
    val jobName = ctx[CoroutineName]?.name ?: "unknown"
    println("[${jobName}] ERROR: ${e.message}")
}
fun main() = runBlocking {
    CoroutineScope(SupervisorJob() + handler).launch(CoroutineName("DataLoader")) {
        throw RuntimeException("fetch failed")
    }
    delay(100)
}

Integrointi kaatumisten raportointiin

Käyttäkää käsittelijää välittääksenne kiinniottamattomat poikkeukset kaatumisten raportointipalveluihin, kuten Firebase Crashlyticsiin tai Sentryyn.

import kotlinx.coroutines.*
object CrashReporter {
    fun record(e: Throwable) = println("[Crashlytics] ${e.message}")
}
val handler = CoroutineExceptionHandler { _, e ->
    if (e !is CancellationException) CrashReporter.record(e)
}
val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + handler)
fun main() = runBlocking {
    appScope.launch { throw RuntimeException("unhandled in production") }
    delay(100)
    appScope.cancel()
}

Käsittelijän periytyminen

Vanhemman kontekstin käsittelijä EI periydy automaattisesti lapsikorutiineille. Sen on oltava juurikorutiinin kontekstissa, jotta se aktivoituu.

import kotlinx.coroutines.*
val handler = CoroutineExceptionHandler { _, e -> println("Handler: ${e.message}") }
fun main() = runBlocking {
    // Handler only fires at root level:
    CoroutineScope(SupervisorJob() + handler).launch {
        // Child of root — exception propagates to root handler:
        launch { throw RuntimeException("nested") }
    }
    delay(100)
}

Parhaiden käytäntöjen yhteenveto

Asentakaa CoroutineExceptionHandler aina sovellustason tai ominaisuustason scopeen. Kirjatkaa jokainen muu kuin peruutukseen liittyvä poikkeus lokiin. Älkää koskaan käyttäkö sitä ohjauslogiikkaan — se on tarkoitettu vain havainnointiin.

import kotlinx.coroutines.*
val globalHandler = CoroutineExceptionHandler { ctx, e ->
    if (e !is CancellationException) {
        println("[ERROR] ${ctx[CoroutineName]?.name}: ${e.message}")
    }
}
val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Default + globalHandler)
fun main() = runBlocking {
    appScope.launch(CoroutineName("Auth")) { throw RuntimeException("token expired") }
    delay(100); appScope.cancel()
}

Pikatarkistus

Minkä tyyppiselle korutiinille CoroutineExceptionHandler EI käynnisty automaattisesti?

Kertaus

CoroutineExceptionHandler on viimeisenä keinona toimiva tarkkailija juurikorutiinien kiinniottamattomille poikkeuksille. Yhdistäkää se SupervisorJob-rakenteeseen pitkäkestoisissa scopeissa. Käyttäkää sitä lokitukseen ja kaatumisten raportointiin — ei koskaan ohjauslogiikkaan.

Aloita maksutta

Opi Kotlin tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
51
Oppitunnit
203

Usein kysytyt kysymykset

Onko oppitunti ”CoroutineExceptionHandler: maailmanlaajuinen käsittelemätön poikkeusten käsittelijä” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Kotlin Academy-oppimispolun 3 oppituntia, myös oppitunnin “CoroutineExceptionHandler: maailmanlaajuinen käsittelemätön poikkeusten käsittelijä”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Kotlin Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”CoroutineExceptionHandler: maailmanlaajuinen käsittelemätön poikkeusten käsittelijä”?

Asentakaa CoroutineExceptionHandler kirjaamaan tai palauttamaan käsittelemättömistä poikkeuksista. Harjoittelet Kotlin Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Kotlin Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Kotlin Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/4.

Kuinka kauan ”CoroutineExceptionHandler: maailmanlaajuinen käsittelemätön poikkeusten käsittelijä”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Kotlin Academy-oppitunnilla?

Kyllä. Jokainen Kotlin Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. SupervisorJob vs Job: virheiden eristäminen
  2. CoroutineExceptionHandler: maailmanlaajuinen käsittelemätön poikkeusten käsittelijä
  3. async/await-poikkeusten eteneminen
  4. Vikasietoisten korutiiniarkkitehtuurien suunnittelu
← Takaisin: Kotlin Academy