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

В какую корутину можно положить ExceptionHandler

3.0 Senior🔥 161 комментариев
#Kotlin основы#Многопоточность и асинхронность

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

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

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

Выбор CoroutineContext для ExceptionHandler

ExceptionHandler в Kotlin Coroutines — это CoroutineContext.Element, который предназначен для глобальной обработки необработанных исключений в корутинах. Он должен быть установлен в контексте CoroutineScope или конкретной корутины. Ключевой момент — ExceptionHandler работает только с корутинами, которые не являются отменяющими (cancellable) исключениями (т.е. не с CancellationException) и когда исключение не было обработано локально с помощью try/catch.

Основные места установки ExceptionHandler

1. GlobalScope или пользовательская область (CoroutineScope)

Наиболее типичный подход — создание пользовательского CoroutineScope с ExceptionHandler в корневом контексте. Это гарантирует, что все корутины, запущенные в этой области, будут использовать обработчик:

val exceptionHandler = CoroutineExceptionHandler { context, throwable ->
    // Логируем или обрабатываем исключение
    Log.e("CoroutineException", "Caught $throwable in context $context")
    // Показываем уведомление пользователю или отправляем в Crashlytics
}

// Создаем пользовательскую область с обработчиком
val customScope = CoroutineScope(
    SupervisorJob() + Dispatchers.IO + exceptionHandler
)

// Все корутины в этой области будут использовать обработчик
customScope.launch {
    throw RuntimeException("Ошибка в корутине")
}

2. Корутины верхнего уровня (launch/async)

ExceptionHandler имеет смысл устанавливать только в корневых корутинах (которые запускаются напрямую из области). Для дочерних корутин он не сработает, так как исключения в иерархии корутин передаются вверх по родительской цепочке:

// Правильно: обработчик в корневой корутине
customScope.launch(exceptionHandler) {
    // Этот обработчик будет действовать
    launch {
        throw IOException("Сетевая ошибка") // Исключение передастся в exceptionHandler
    }
}

// Неправильно: обработчик в дочерней корутине (не сработает)
customScope.launch {
    launch(exceptionHandler) {
        throw Exception("Это исключение НЕ будет поймано ExceptionHandler")
    }.join()
}

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

  1. SupervisorJob для независимых корутин Если вы хотите, чтобы исключение в одной корутине не отменяло другие корутины в той же области, используйте SupervisorJob():
val scope = CoroutineScope(
    SupervisorJob() + Dispatchers.Main + exceptionHandler
)

scope.launch {
    // Если здесь будет исключение, другие корутины scope продолжат работу
}
  1. Исключения в async/await Для корутин, запущенных через async, исключение будет отложено до вызова await(). ExceptionHandler сработает, только если исключение не было обработано при вызове await():
scope.launch(exceptionHandler) {
    val deferred = async {
        throw IllegalArgumentException("Ошибка в async")
    }
    
    try {
        deferred.await() // ExceptionHandler НЕ сработает, так как исключение обработано здесь
    } catch (e: Exception) {
        // Локальная обработка
    }
}
  1. Android-специфика В Android часто используют ExceptionHandler в области жизненного цикла (ViewModel, Activity):
class MyViewModel : ViewModel() {
    private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
        // Отправляем в Firebase Crashlytics или показываем сообщение
        _errorEvent.postValue(throwable.message)
    }
    
    private val viewModelScope = CoroutineScope(
        viewModelScope.coroutineContext + exceptionHandler
    )
    
    fun fetchData() {
        viewModelScope.launch {
            // Выполнение сетевого запроса
        }
    }
}

Альтернативы ExceptionHandler

  • Локальная обработка с try/catch внутри корутины — предпочтительнее для контролируемых ошибок.
  • SupervisorScope — для создания изолированных групп корутин.
  • Классы-обертки для корутин с встроенной обработкой ошибок.

Вывод

ExceptionHandler следует устанавливать в CoroutineContext корневого CoroutineScope или корневой корутины, используя SupervisorJob() для изоляции ошибок. В Android это часто делают в пользовательских областях ViewModel или Application-класса. Помните, что это механизм последней линии защиты — для предсказуемых ошибок лучше использовать структурированную обработку исключений внутри корутин.