0Pricing
Kotlin Academy · Урок

CoroutineExceptionHandler: глобальный обработчик неперехваченных исключений

Устанавливайте CoroutineExceptionHandler для записи в журнал или восстановления после необработанных исключений.

«CoroutineExceptionHandler: глобальный обработчик неперехваченных исключений» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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()
    }
}

Обработчик с 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 исключения сохраняются в 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()
}

Сравнение с UncaughtExceptionHandler потока

В отличие от UncaughtExceptionHandler в Java, обработчик 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

В серверных приложениях обработчик может извлечь из контекста корутины такие сведения, как идентификаторы запросов, чтобы вести структурированные журналы перед передачей данных в систему ведения журналов.

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/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «CoroutineExceptionHandler: глобальный обработчик неперехваченных исключений»?

Устанавливайте CoroutineExceptionHandler для записи в журнал или восстановления после необработанных исключений. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «CoroutineExceptionHandler: глобальный обработчик неперехваченных исключений»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. SupervisorJob и Job: изоляция сбоев
  2. CoroutineExceptionHandler: глобальный обработчик неперехваченных исключений
  3. Распространение исключений в async/await
  4. Проектирование устойчивых архитектур сопрограмм
← Назад к Kotlin Academy