← Назад к вопросам
В чем разница между горячим и холодным Flow?
2.0 Middle🔥 211 комментариев
#Многопоточность и асинхронность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
В чем разница между горячим и холодным Flow
Определение
Холодный Flow (cold flow) — каждый подписчик получает свою копию потока данных, начиная с начала. Горячий Flow (hot flow) — все подписчики получают один и тот же поток, независимо от того, когда они подписались.
1. Холодный Flow (Cold Flow)
Холодный flow создает новый поток для каждого подписчика. Это Flow по умолчанию в Kotlin.
fun getColdFlow(): Flow<Int> = flow {
println("Flow started")
emit(1)
emit(2)
emit(3)
println("Flow ended")
}
launchIO {
println("Подписчик 1:")
getColdFlow().collect { value ->
println("Подписчик 1 получил: $value")
}
}
launchIO {
println("Подписчик 2:")
getColdFlow().collect { value ->
println("Подписчик 2 получил: $value")
}
}
// Вывод:
// Подписчик 1:
// Flow started
// Подписчик 1 получил: 1
// Подписчик 1 получил: 2
// Подписчик 1 получил: 3
// Flow ended
// Подписчик 2:
// Flow started <- Новый flow для второго подписчика!
// Подписчик 2 получил: 1
// Подписчик 2 получил: 2
// Подписчик 2 получил: 3
// Flow ended
Характеристики:
- Каждая подписка создает новый поток
- Подписчик получает все данные с начала
- Ленивый (lazy) — не выполняется, пока нет подписчика
- Низкое потребление ресурсов — выполняется по требованию
2. Горячий Flow (Hot Flow)
Горячий flow существует независимо от подписчиков. Используется StateFlow или SharedFlow.
val hotFlow = MutableSharedFlow<Int>()
launchIO {
println("Подписчик 1:")
hotFlow.collect { value ->
println("Подписчик 1 получил: $value")
}
}
launchIO {
emit(1)
emit(2)
emit(3)
}
delay(100) // Ждем перед второй подпиской
launchIO {
println("Подписчик 2:")
hotFlow.collect { value ->
println("Подписчик 2 получил: $value"
}
}
// Вывод:
// Подписчик 1:
// Подписчик 1 получил: 1
// Подписчик 1 получил: 2
// Подписчик 1 получил: 3
// Подписчик 2: <- Запоздал, не получит старые значения!
Характеристики:
- Один поток для всех подписчиков
- Подписчики получают только новые данные после подписки
- Горячий (hot) — выполняется независимо от подписчиков
- Высокое потребление ресурсов — выполняется всегда
3. StateFlow (особый случай горячего потока)
StateFlow — это горячий поток, который хранит текущее состояние и отправляет его новым подписчикам.
val stateFlow = MutableStateFlow(0)
// Новый подписчик СРАЗУ получит текущее значение!
stateFlow.collect { value ->
println("Получено: $value")
}
stateFlow.value = 1 // Все подписчики получат обновление
Таблица сравнения:
| Аспект | Cold Flow | Hot Flow (SharedFlow) | Hot Flow (StateFlow) |
|---|---|---|---|
| Создание потока | Каждому подписчику | Один на всех | Один на всех |
| Новый подписчик получает | Все данные с начала | Только новые | Текущее значение + новые |
| Потребление ресурсов | Низкое | Высокое | Высокое |
| Пример | Запрос к API | События пользователя | Состояние UI |
4. Практические примеры
Холодный Flow: Загрузка данных
class UserRepository {
fun getUsers(): Flow<List<User>> = flow {
val users = apiClient.fetchUsers() // Каждый подписчик запросит API!
emit(users)
}
}
class UserViewModel : ViewModel() {
private val _users = MutableStateFlow<List<User>>(emptyList())
val users = _users.asStateFlow()
fun loadUsers() {
viewModelScope.launch {
// Каждый раз при loadUsers() будет новый запрос
repository.getUsers().collect { users ->
_users.value = users
}
}
}
}
Горячий Flow: События пользователя
class EventBus {
private val _events = MutableSharedFlow<Event>()
val events = _events.asSharedFlow()
suspend fun sendEvent(event: Event) {
_events.emit(event) // Отправляется всем подписчикам
}
}
class EventFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
eventBus.events.collect { event ->
handleEvent(event)
}
}
}
}
StateFlow: Состояние приложения
class AuthViewModel : ViewModel() {
private val _authState = MutableStateFlow<AuthState>(AuthState.Idle)
val authState = _authState.asStateFlow() // Читаемый StateFlow
fun login(email: String, password: String) {
viewModelScope.launch {
_authState.value = AuthState.Loading
try {
val user = apiClient.login(email, password)
_authState.value = AuthState.Success(user)
} catch (e: Exception) {
_authState.value = AuthState.Error(e.message ?: "Unknown error")
}
}
}
}
class LoginFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.authState.collect { state ->
when (state) {
AuthState.Idle -> {}
AuthState.Loading -> showProgressBar()
is AuthState.Success -> navigateToHome()
is AuthState.Error -> showError(state.message)
}
}
}
}
}
5. Когда использовать
Используй холодный Flow когда:
- Запрос к API или базе данных
- Трансформация данных
- Каждый подписчик должен получить полный результат
- Нужно экономить ресурсы (lazy evaluation)
fun getFilteredUsers(searchQuery: String): Flow<List<User>> = flow {
val users = repository.getAllUsers()
val filtered = users.filter { it.name.contains(searchQuery) }
emit(filtered)
}
Используй горячий Flow когда:
- Буду работать с событиями (клики, навигация)
- Несколько подписчиков должны получить одно событие
- Потребление ресурсов не критично
val navigationEvents = MutableSharedFlow<NavigationEvent>()
button.setOnClickListener {
viewModelScope.launch {
navigationEvents.emit(NavigationEvent.OpenProfile)
}
}
Используй StateFlow когда:
- Хранишь состояние UI
- Новому подписчику нужно текущее значение
- Нужен один источник истины для состояния
val uiState = MutableStateFlow(UiState())
val currentState get() = uiState.value
6. Оптимизация: shareIn() для превращения холодного в горячий
class CachedUserRepository {
private val apiFlow = flow {
emit(apiClient.getUsers())
}.shareIn( // Преобразуем холодный в горячий
scope = viewModelScope,
started = SharingStarted.Lazily, // Начать при первой подписке
replay = 1 // Сохранить последний результат
)
fun getUsers(): Flow<List<User>> = apiFlow
}
// Теперь несколько подписчиков поделят один запрос к API!
Итог
Холодный Flow:
- Каждый подписчик = новый поток
- Получает все данные с начала
- Ленивый, экономный
- По умолчанию в Kotlin
Горячий Flow (SharedFlow/StateFlow):
- Один поток на всех
- Только новые данные
- Горячий, всегда работает
- Используй для событий и состояния
StateFlow особенный:
- Горячий поток с памятью
- Новый подписчик получит текущее значение
- Лучший выбор для UI состояния