В какую корутину можно положить ExceptionHandler
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Выбор 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()
}
Критически важные нюансы использования
- SupervisorJob для независимых корутин
Если вы хотите, чтобы исключение в одной корутине не отменяло другие корутины в той же области, используйте
SupervisorJob():
val scope = CoroutineScope(
SupervisorJob() + Dispatchers.Main + exceptionHandler
)
scope.launch {
// Если здесь будет исключение, другие корутины scope продолжат работу
}
- Исключения в async/await
Для корутин, запущенных через
async, исключение будет отложено до вызоваawait().ExceptionHandlerсработает, только если исключение не было обработано при вызовеawait():
scope.launch(exceptionHandler) {
val deferred = async {
throw IllegalArgumentException("Ошибка в async")
}
try {
deferred.await() // ExceptionHandler НЕ сработает, так как исключение обработано здесь
} catch (e: Exception) {
// Локальная обработка
}
}
- 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-класса. Помните, что это механизм последней линии защиты — для предсказуемых ошибок лучше использовать структурированную обработку исключений внутри корутин.