0Pricing
Kotlin Academy · レッスン

CoroutineExceptionHandler:グローバルな未捕捉例外ハンドラー

CoroutineExceptionHandlerを設定し、未処理の例外をログに記録または復旧します。

「CoroutineExceptionHandler:グローバルな未捕捉例外ハンドラー」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

CoroutineExceptionHandler とは

CoroutineExceptionHandler は、キャッチハンドラーを持たないコルーチンからの未処理例外を処理するコンテキスト要素です。最後の手段として機能します。

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)
}

ルートコルーチン専用

CoroutineExceptionHandler が捕捉するのは、ルートコルーチン(スコープ上で直接起動されたコルーチン)からの例外だけです。子コルーチンの例外はハンドラーではなく親に伝播します。

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() } }
}

例外を抑制するものではない

ハンドラーが呼び出されるのは、例外によってコルーチンがすでにキャンセルされた後です。ハンドラーはログ記録、クラッシュレポート、クリーンアップのためのものであり、実行を再開するためのものではありません。

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()
    }
}

Handler と SupervisorJob

SupervisorJob + CoroutineExceptionHandler の組み合わせは、長期間存続するスコープの標準的なパターンです。子は独立して失敗し、未処理の失敗は記録されます。

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()
}

async と Handler

async では、例外は Deferred に保存され、await() でスローされます。Deferred が await されない場合を除き、async でハンドラーが発火することはありません。

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 との比較

Java の UncaughtExceptionHandler とは異なり、Kotlin のハンドラーはコルーチンコンテキストの一部であり、そのスコープ内のコルーチンにのみ適用されます。

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") }

MDC を使ったロギングパターン

サーバーアプリでは、ロギングフレームワークに渡す前に、ハンドラーでリクエストIDなどのコルーチンコンテキスト情報を取得し、構造化ロギングに利用できます。

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)
}

クラッシュレポートとの統合

ハンドラーを使用して、未処理の例外を Firebase Crashlytics や Sentry などのクラッシュレポートサービスに転送します。

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()
}

ハンドラーの継承

親コンテキストのハンドラーは、子コルーチンに自動的には継承されません。発火させるには、ルートコルーチンのコンテキストに設定する必要があります。

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)
}

ベストプラクティスのまとめ

アプリレベルまたは機能レベルのスコープには、必ず CoroutineExceptionHandler を設定します。キャンセル以外の例外はすべて記録してください。ハンドラーを制御フローに利用してはいけません。ハンドラーは可観測性のためだけのものです。

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()
}

クイックチェック

CoroutineExceptionHandler が自動的に発火しないコルーチンの種類はどれですか。

振り返り

CoroutineExceptionHandler は、ルートコルーチンで発生した未処理例外を監視する最後の手段です。長期間存続するスコープでは SupervisorJob と組み合わせます。ログ記録やクラッシュレポートに使用し、制御フローには決して使用しません。

よくある質問

「CoroutineExceptionHandler:グローバルな未捕捉例外ハンドラー」レッスンは無料ですか?

はい。「CoroutineExceptionHandler:グローバルな未捕捉例外ハンドラー」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「CoroutineExceptionHandler:グローバルな未捕捉例外ハンドラー」で何を学びますか?

CoroutineExceptionHandlerを設定し、未処理の例外をログに記録または復旧します。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「CoroutineExceptionHandler:グローバルな未捕捉例外ハンドラー」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. SupervisorJobとJob:失敗の分離
  2. CoroutineExceptionHandler:グローバルな未捕捉例外ハンドラー
  3. async/awaitによる例外の伝播
  4. 堅牢なコルーチンアーキテクチャの設計
← Kotlin Academyに戻る