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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- SupervisorJobとJob:失敗の分離
- CoroutineExceptionHandler:グローバルな未捕捉例外ハンドラー
- async/awaitによる例外の伝播
- 堅牢なコルーチンアーキテクチャの設計