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

Как можно поймать исключение в корутинах?

2.0 Middle🔥 112 комментариев
#Многопоточность и асинхронность

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

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

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

Обработка исключений в Kotlin Coroutines

Обработка исключений в корутинах — критически важный аспект для создания стабильных Android-приложений. В отличие от традиционных потоков, корутины предоставляют несколько механизмов обработки ошибок, каждый со своей семантикой.

Основные подходы к обработке исключений

1. try-catch внутри корутины

Стандартный подход, работающий аналогично обычному коду:

viewModelScope.launch {
    try {
        val result = repository.fetchData() // Может выбросить исключение
        processResult(result)
    } catch (e: IOException) {
        // Обработка сетевых ошибок
        showError("Network error: ${e.message}")
    } catch (e: Exception) {
        // Общая обработка
        showError("Unexpected error")
    }
}

2. CoroutineExceptionHandler для корутин верхнего уровня

Глобальный обработчик для корутин, запущенных через launch:

val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
    // Логирование или уведомление об ошибке
    Log.e("CoroutineError", "Unhandled exception", throwable)
    showGlobalError(throwable)
}

// Использование с launch
viewModelScope.launch(exceptionHandler) {
    throw IOException("Network failure")
}

Важно: CoroutineExceptionHandler работает только с корутинами, запущенными через launch, а не через async.

3. Обработка исключений в async/await

Для конструкций async исключения откладываются до вызова await():

viewModelScope.launch {
    val deferred = async {
        fetchData() // Может выбросить исключение
    }
    
    try {
        val result = deferred.await()
        processResult(result)
    } catch (e: Exception) {
        // Обработка исключения из async
        handleError(e)
    }
}

SupervisorJob и SupervisorScope

Обычный Job отменяет все дочерние корутины при ошибке в одной из них. SupervisorJob меняет это поведение:

// С SupervisorJob ошибка в одной корутине не отменяет другие
val supervisorJob = SupervisorJob()
val scope = CoroutineScope(Dispatchers.Main + supervisorJob)

scope.launch {
    // Эта корутина упадет
    throw RuntimeException("Error 1")
}

scope.launch {
    // Эта продолжит работу даже после падения предыдущей
    delay(1000)
    println("I'm still alive!")
}

SupervisorScope предоставляет то же поведение в виде области видимости:

supervisorScope {
    launch {
        // Не зависит от других корутин в этом scope
        throw IOException()
    }
    
    launch {
        // Будет продолжено выполнение
        delay(1000)
        fetchData()
    }
}

Особенности обработки исключений

Отмена корутин

  • При отмене корутина выбрасывает CancellationException
  • Это исключение игнорируется обработчиками и не логируется
  • Для реагирования на отмену используйте finally или проверку isActive
job = launch {
    try {
        longRunningOperation()
    } finally {
        // Выполнится даже при отмене
        cleanupResources()
        if (isActive) {
            // Только если не была отменена
            updateUI()
        }
    }
}

Комбинирование подходов

На практике часто используются комбинации:

val handler = CoroutineExceptionHandler { _, e ->
    analytics.logError(e)
}

viewModelScope.launch(handler) {
    supervisorScope {
        val userData = async { fetchUserData() }
        val settings = async { fetchSettings() }
        
        try {
            val combinedData = combineData(userData.await(), settings.await())
            updateUI(combinedData)
        } catch (e: CompositeException) {
            // Обработка частичных сбоев
            showPartialData(e)
        }
    }
}

Рекомендации для Android-разработки

  1. Используйте SupervisorJob для независимых операций (например, несколько параллельных запросов к разным API)
  2. Логируйте необработанные исключения через CoroutineExceptionHandler для отладки
  3. Разделяйте ответственность: бизнес-логика должна пробрасывать исключения, UI-слой — обрабатывать
  4. Тестируйте сценарии с ошибками с помощью TestCoroutineDispatcher
  5. Для глобальной обработки создайте базовый класс ViewModel с общим обработчиком ошибок

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

Как можно поймать исключение в корутинах? | PrepBro