← Назад к вопросам
Как обработать ошибки Flow
1.7 Middle🔥 241 комментариев
#Kotlin основы#Многопоточность и асинхронность
Комментарии (1)
🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Обработка ошибок в Flow Kotlin
Flow в Kotlin — это cold stream асинхронных данных, и правильная обработка ошибок крайне важна для создания стабильных приложений. Вот основные подходы:
1. Операторы для обработки ошибок
catch — основной оператор
Позволяет перехватывать исключения, возникшие выше по цепочке:
flow {
emit(1)
throw RuntimeException("Ошибка!")
}
.catch { cause ->
// Логируем ошибку
Log.e("Flow", "Ошибка: ${cause.message}")
// Можно emit альтернативные данные
emit(-1)
}
.collect { value ->
println("Получено: $value")
}
retry и retryWhen — повторные попытки
flow { /* сетевой запрос */ }
.retry(3) { cause ->
// Повторяем только для сетевых ошибок
cause is IOException
}
.retryWhen { cause, attempt ->
// Кастомизированная логика повтора
delay(100 * attempt)
attempt < 3 && cause is NetworkException
}
2. Комбинация с жизненным циклом
В Android важно связывать Flow с жизненным циклом:
class ViewModel : ViewModel() {
val dataFlow = repository.getData()
.catch { cause ->
_errorState.value = cause.toUiError()
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = null
)
}
3. Стратегии обработки разных типов ошибок
Business errors (ожидаемые ошибки):
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val message: String) : Result<Nothing>()
}
fun getData(): Flow<Result<Data>> = flow {
try {
val data = apiService.fetchData()
emit(Result.Success(data))
} catch (e: BusinessException) {
emit(Result.Error(e.localizedMessage))
}
}
Fatal errors (критические ошибки):
.catch { cause ->
when (cause) {
is NetworkException -> emit(ErrorState.NetworkError)
is DatabaseException -> {
logCrash(cause)
throw cause // Пробрасываем критическую ошибку
}
else -> emit(ErrorState.UnknownError)
}
}
4. SupervisorJob для изоляции ошибок
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
flow1.catch { /* ошибка flow1 не убьет flow2 */ }
flow2.catch { /* независимая обработка */ }
5. Практический паттерн для ViewModel
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun loadData() {
viewModelScope.launch {
repository.getDataFlow()
.onStart { _uiState.value = UiState.Loading }
.catch { cause ->
_uiState.value = UiState.Error(
message = cause.message ?: "Ошибка",
retryAction = { loadData() }
)
}
.collect { data ->
_uiState.value = UiState.Success(data)
}
}
}
}
6. Тестирование обработки ошибок
@Test
fun `should emit error state when network fails`() = runTest {
val flow = flow<Int> { throw IOException() }
.catch { emit(-1) }
val results = mutableListOf<Int>()
flow.toList(results)
assertEquals(listOf(-1), results)
}
Ключевые рекомендации:
- Всегда обрабатывайте ошибки явно — не полагайтесь на неявное пробрасывание
- Разделяйте бизнес-ошибки и системные ошибки — первые часто часть UI состояния, вторые требуют логирования
- Используйте sealed classes для представления состояний с ошибками
- Связывайте с жизненным циклом в Android — отменяйте коллекцию при destroy
- Логируйте критические ошибки для последующего анализа
- Предусмотрите retry логику для сетевых операций
- Изолируйте независимые потоки через SupervisorJob
Правильная обработка ошибок в Flow делает приложение отказоустойчивым, улучшает пользовательский опыт и упрощает отладку проблем в продакшене.