В чем разница горячих и холодных потоков в Kotlin Flow
Что проверяют
Понимаете ли вы, когда код внутри потока реально выполняется и сколько раз. Для Android это практика: от ответа зависит, уйдёт один сетевой запрос или три. Следом обычно спрашивают, чем StateFlow отличается от SharedFlow и как превратить холодный поток в горячий.
Как отвечать
- холодный поток: обычный
Flowиз билдераflow {},flowOf,asFlow, потоки из Room. Блок ничего не делает, пока нетcollect, и выполняется заново для каждого коллектора: два подписчика означают два запроса; - горячий поток:
SharedFlowиStateFlow. Существуют независимо от подписчиков, все подписчики получают одни и те же значения, а опоздавший видит только то, что сохранено вreplay.collectна горячем потоке сам не завершается никогда; StateFlow: всегда хранит текущееvalue, требует начальное значение, пропускает повторы (равное значение не эмитится) и медленному подписчику отдаёт только последнее. Это состояние экрана;SharedFlow: настраивается черезreplay,extraBufferCapacityиonBufferOverflow. Сreplay = 0значение, отправленное без подписчиков, просто теряется;- переход:
shareInиstateInделают из холодного горячий. ВViewModelтипичная записьstateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), initial): пять секунд переживают поворот экрана, но не держат источник в фоне.
На стороне UI собирают через repeatOnLifecycle(Lifecycle.State.STARTED) или collectAsStateWithLifecycle() в Compose. BroadcastChannel и GlobalScope из старых примеров как норму не приводите: первый устарел, второй не привязан к жизненному циклу.
Где спотыкаются
Отдают из репозитория холодный flow {} в два места экрана и удивляются двойным запросам. Нужен shareIn или stateIn в общем скоупе.
Кладут одноразовые события (навигация, снэкбар) в StateFlow: после поворота событие приходит снова, а два одинаковых подряд схлопываются. Для событий берут Channel с receiveAsFlow() или хранят событие в состоянии и сбрасывают после обработки.
Собирают в lifecycleScope.launch без repeatOnLifecycle: подписка работает и в фоне, а с SharingStarted.Eagerly источник не останавливается вообще.