Как обрабатывать исключения корутин в Kotlin?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Обработка исключений в Kotlin Coroutines
Обработка исключений в корутинах — критически важный аспект для создания стабильных Android-приложений. Механизм отличается от традиционной обработки исключений в потоках, поскольку корутины используют структурированный параллелизм и иерархию отмены.
Основные подходы к обработке
1. Try-Catch внутри корутины
Самый простой способ — обрабатывать исключения непосредственно в блоке кода корутины:
viewModelScope.launch {
try {
val result = repository.fetchData() // Может выбросить исключение
updateUi(result)
} catch (e: IOException) {
showError("Ошибка сети: ${e.message}")
} catch (e: IllegalArgumentException) {
showError("Неверные данные: ${e.message}")
}
}
2. CoroutineExceptionHandler для корутин верхнего уровня
Для обработки непойманных исключений в корутинах верхнего уровня используйте CoroutineExceptionHandler:
val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
println("Необработанное исключение: ${throwable.message}")
// Отправляем в Crashlytics/аналитику
FirebaseCrashlytics.getInstance().recordException(throwable)
}
// Использование с launch
viewModelScope.launch(exceptionHandler) {
throw IOException("Сетевая ошибка")
}
Важно: CoroutineExceptionHandler работает только с корутинами, запущенными через launch, а не async.
3. Обработка в async/await конструкциях
Для async корутин исключения обрабатываются при вызове await():
viewModelScope.launch {
val deferred = async {
repository.fetchData() // Может выбросить исключение
}
try {
val result = deferred.await()
updateUi(result)
} catch (e: Exception) {
handleError(e)
}
}
Ключевые особенности и лучшие практики
Распространение исключений
- Исключения, не обработанные внутри корутины, автоматически распространяются вверх по иерархии
- Родительская корутина отменяется при необработанном исключении в дочерней (по умолчанию)
- Все дочерние корутины также отменяются
SupervisorJob для изолированной обработки
Используйте SupervisorJob или supervisorScope, когда нужно предотвратить отмену родительской корутины при исключении в дочерней:
// С SupervisorJob - исключения в дочерних корутинах не отменяют родителя
val supervisorJob = SupervisorJob()
val scope = CoroutineScope(Dispatchers.Main + supervisorJob)
scope.launch {
// Эта корутина выполнится, даже если вторая упадет
launch { fetchUserData() }
launch {
// Исключение здесь не отменит первую корутину
throw IOException("Ошибка в параллельной задаче")
}
}
// Или с supervisorScope
viewModelScope.launch {
supervisorScope {
launch { fetchData1() }
launch { fetchData2() } // Исключение здесь не затронет fetchData1()
}
}
Различия между launch и async
launch: Исключения немедленно выбрасываются и могут быть обработаныCoroutineExceptionHandlerasync: Исключения откладываются до вызоваawait()и должны быть обработаны там
Паттерны для Android-разработки
ViewModel с обработкой состояния
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState
fun loadData() {
viewModelScope.launch {
_uiState.value = UiState.Loading
try {
val data = repository.loadData()
_uiState.value = UiState.Success(data)
} catch (e: Exception) {
_uiState.value = UiState.Error(e.message ?: "Unknown error")
// Логирование для аналитики
logException(e)
}
}
}
}
Global Exception Handler
Создайте глобальный обработчик для всего приложения:
object GlobalCoroutineExceptionHandler {
private val handler = CoroutineExceptionHandler { context, exception ->
// Логирование
Timber.e(exception, "Global coroutine exception")
// Отправка в аналитику
FirebaseCrashlytics.getInstance().recordException(exception)
// Показ уведомления пользователю (если нужно)
if (exception is NetworkException) {
showGlobalErrorNotification()
}
}
fun getHandler() = handler
}
Рекомендации по обработке конкретных типов исключений
- Сетевые ошибки — используйте retry-механизмы с экспоненциальной задержкой
- Ошибки валидации — показывайте пользователю понятные сообщения
- Критические ошибки — логируйте и отправляйте в системы мониторинга
- Отмена корутин — обрабатывайте
CancellationExceptionотдельно, так как это нормальный механизм завершения
Правильная обработка исключений в корутинах обеспечивает стабильность приложения, улучшает пользовательский опыт и упрощает отладку проблем в продакшене. Всегда комбинируйте несколько подходов в зависимости от контекста и критичности операций.