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

Какие знаешь Flow?

1.6 Junior🔥 112 комментариев
#Kotlin основы#Многопоточность и асинхронность

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

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

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

Обзор типов Flow в Kotlin Coroutines

В контексте Kotlin Coroutines существует несколько типов Flow, каждый из которых предназначен для решения определённых задач асинхронной обработки потоков данных.

1. Cold Flow (холодный поток)

Это стандартный Flow<T>, который начинает испускать данные только когда запускается коллектор (collect). Каждый коллектор получает собственную независимую последовательность данных.

fun simpleColdFlow(): Flow<Int> = flow {
    println("Flow started")
    for (i in 1..3) {
        delay(100)
        emit(i)
    }
}

// При запуске:
fun main() = runBlocking {
    simpleColdFlow().collect { value -> println(value) }  // Flow started, затем 1,2,3
    simpleColdFlow().collect { value -> println(value) }  // Снова Flow started, затем 1,2,3
}

2. Hot Flow (горячий поток)

Горячие потоки начинают испускать данные независимо от наличия коллекторов. Самый распространённый представитель — SharedFlow и StateFlow.

SharedFlow

val sharedFlow = MutableSharedFlow<Int>(
    replay = 2, // количество перепосылаемых последних значений новым подписчикам
    extraBufferCapacity = 10 // буфер для значений при медленном коллекторе
)

// Излучаем значения
launch {
    sharedFlow.emit(1)
    sharedFlow.emit(2)
}

// Подписываемся позже - получим последние значения из replay
launch {
    sharedFlow.collect { value -> println("Collected: $value") }
}

StateFlow

Специализированный SharedFlow с обязательным начальным значением и дистинкцией (отправляет только уникальные значения подряд).

val stateFlow = MutableStateFlow(0) // начальное значение обязательно

// Изменяем состояние
stateFlow.value = 1
stateFlow.value = 2
stateFlow.value = 2 // Это значение не будет отправлено (дистинкция)

// Коллектор всегда получает последнее значение сразу при подписке
stateFlow.collect { value -> println("State: $value") }

3. StateFlow vs SharedFlow: ключевые различия

  • StateFlow всегда имеет начальное значение, SharedFlow — нет
  • StateFlow выполняет дистинкцию (не повторяет одинаковые значения подряд)
  • StateFlow сохраняет только последнее значение, SharedFlow может хранить историю через replay
  • StateFlow подходит для состояния UI, SharedFlow — для событий

4. Flow операторы и комбинации

Kotlin предоставляет богатый набор операторов для работы с Flow:

// Преобразования
flow.map { it * 2 }
flow.filter { it > 0 }
flow.transform { emit(it * 2); emit(it * 3) }

// Комбинация нескольких потоков
flow1.zip(flow2) { a, b -> a + b }
flow1.combine(flow2) { a, b -> a + b }

// Обработка ошибок
flow.catch { e -> emit("Error: ${e.message}") }
flow.retry(3) { cause -> cause is IOException }

// Контекст и многопоточность
flow.flowOn(Dispatchers.IO)
flow.buffer() // буферизация для разделения производства и потребления

5. Callback Flow

Специальный тип для преобразования callback-based API в Flow:

fun locationFlow(): Flow<Location> = callbackFlow {
    val callback = object : LocationCallback() {
        override fun onLocationResult(result: LocationResult) {
            result.locations.forEach { location ->
                trySend(location)
            }
        }
    }
    
    locationClient.requestLocationUpdates(request, callback, Looper.getMainLooper())
    
    awaitClose {
        locationClient.removeLocationUpdates(callback)
    }
}

6. Channel Flow

Гибрид между Channel и Flow для более сложных сценариев:

fun channelFlowExample(): Flow<Int> = channelFlow {
    // Можно отправлять данные из разных корутин
    launch {
        send(1)
    }
    launch {
        send(2)
    }
}

Практические рекомендации по выбору Flow:

  • Используйте обычный Flow для холодных потоков данных (запросы к БД, работа с файлами)
  • Применяйте StateFlow для хранения и наблюдения за состоянием UI
  • Выбирайте SharedFlow для событий (нажатия кнопок, системные события)
  • Используйте callbackFlow для интеграции с legacy callback API
  • ChannelFlow подходит для сложных многопоточных сценариев

Производительность и best practices:

  • Избегайте создания Flow в циклах или частых вызовах
  • Используйте flowOn для тяжёлых операций в фоновом потоке
  • Применяйте buffer() при несовпадении скоростей производства и потребления
  • Всегда обрабатывайте ошибки через catch или retry
  • Для StateFlow/SharedFlow используйте stateIn/shareIn для конвертации cold в hot flow с управлением областью видимости

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