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

Как обработать ошибки 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)
}

Ключевые рекомендации:

  1. Всегда обрабатывайте ошибки явно — не полагайтесь на неявное пробрасывание
  2. Разделяйте бизнес-ошибки и системные ошибки — первые часто часть UI состояния, вторые требуют логирования
  3. Используйте sealed classes для представления состояний с ошибками
  4. Связывайте с жизненным циклом в Android — отменяйте коллекцию при destroy
  5. Логируйте критические ошибки для последующего анализа
  6. Предусмотрите retry логику для сетевых операций
  7. Изолируйте независимые потоки через SupervisorJob

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

Как обработать ошибки Flow | PrepBro