← Назад к вопросам

Для чего используется CoroutineExceptionHandler?

1.7 Middle🔥 182 комментариев
#Kotlin основы#Архитектура и паттерны#Многопоточность и асинхронность

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Назначение CoroutineExceptionHandler

CoroutineExceptionHandler — это механизм обработки непойманных исключений в корутинах Kotlin. Его основное предназначение — предоставить централизованный и структурированный способ перехвата и обработки исключений, которые не были обработаны внутри самой корутины с помощью try/catch.

Ключевые аспекты использования

1. Обработка непойманных исключений

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

val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
    println("Поймано исключение: ${throwable.message}")
    // Логирование, отправка в Crashlytics, etc.
}

fun main() = runBlocking {
    val job = launch(exceptionHandler) {
        throw RuntimeException("Что-то пошло не так!")
    }
    job.join()
}

2. Особенности работы с разными билдерами

Важно понимать, что CoroutineExceptionHandler работает только с корутинами, запущенными через launch. Для корутин, созданных через async, исключения не попадают в хендлер напрямую, а оборачиваются в Deferred и должны быть обработаны при вызове await().

val handler = CoroutineExceptionHandler { _, e ->
    println("Обработчик сработает только для launch")
}

fun example() = runBlocking {
    // Исключение попадет в handler
    val job1 = launch(handler) {
        throw Exception("Ошибка в launch")
    }
    
    // Исключение НЕ попадет в handler - будет обернуто в Deferred
    val deferred = async(handler) {
        throw Exception("Ошибка в async")
    }
    
    try {
        deferred.await() // Исключение будет выброшено здесь
    } catch (e: Exception) {
        println("Поймано исключение из async: ${e.message}")
    }
}

3. Иерархия контекстов и наследование

CoroutineExceptionHandler является элементом контекста корутины и подчиняется правилам наследования контекстов. Если хендлер установлен в родительской корутине, дочерние корутины могут его наследовать.

val parentHandler = CoroutineExceptionHandler { _, e ->
    println("Родительский обработчик: ${e.message}")
}

fun hierarchyExample() = runBlocking {
    val parentJob = launch(parentHandler) {
        // Дочерняя корутина наследует контекст, включая exceptionHandler
        launch {
            throw RuntimeException("Ошибка в дочерней корутине")
        }
    }
    parentJob.join()
}

Практические сценарии использования

Логирование и мониторинг

  • Запись информации об ошибках в лог-файлы
  • Отправка отчетов об ошибках в системы мониторинга (Firebase Crashlytics, Sentry, etc.)
  • Сбор аналитики по типам и частоте ошибок

Глобальная обработка ошибок UI

val uiExceptionHandler = CoroutineExceptionHandler { _, exception ->
    withContext(Dispatchers.Main) {
        showErrorMessageToUser(exception)
        // Показать диалог, snackbar или обновить UI
    }
}

Восстановление после сбоев

  • Автоматический повтор запросов при сетевых ошибках
  • Переключение на резервные источники данных
  • Сохранение состояния приложения перед падением

Интеграция с ViewModel в Android

class MyViewModel : ViewModel() {
    private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
        _errorState.value = throwable.message
    }
    
    private val viewModelScope = CoroutineScope(
        SupervisorJob() + Dispatchers.Main + exceptionHandler
    )
    
    fun loadData() {
        viewModelScope.launch {
            // Бизнес-логика, которая может выбросить исключение
            val data = repository.fetchData()
            _dataState.value = data
        }
    }
}

Важные ограничения и лучшие практики

  • Не для отмены корутин: CoroutineExceptionHandler не предназначен для отмены корутин — для этого используйте CancellationException
  • Только для корутин-тасок: Работает только с корутинами, которые являются "task" (запущенными через launch)
  • Супервизор-джобы: В сочетании с SupervisorJob позволяет обрабатывать ошибки в дочерних корутинах независимо
  • Не блокировать в хендлере: Избегайте долгих операций в самом обработчике

Заключение

CoroutineExceptionHandler — это мощный инструмент для создания отказоустойчивых асинхронных приложений на Kotlin. Он позволяет централизованно управлять ошибками, обеспечивая стабильность приложения и улучшая пользовательский опыт. Правильное использование этого механизма в сочетании с другими элементами структуры корутин (как SupervisorJob, структурированная конкурентность) является ключевым навыком для Android-разработчиков, работающих с асинхронным кодом.