Какие знаешь горячие Flow в Coroutines?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Горячие Flow в Kotlin Coroutines
Горячие потоки (Hot Flows) — это потоки данных, которые эмитят значения независимо от наличия подписчиков. В отличие от холодных Flow, горячие потоки работают всегда и переиспользуют данные между разными подписчиками.
Cold vs Hot Flow
Cold Flow — каждый подписчик получает свою копию данных:
val flow = flow {
emit(1)
emit(2)
emit(3)
}
flow.collect { println("Collector1: $it") }
// Output: 1, 2, 3
flow.collect { println("Collector2: $it") }
// Output: 1, 2, 3 (ещё раз! Функция flow выполнилась дважды)
Hot Flow — данные эмитят один раз для всех подписчиков:
val hotFlow = MutableSharedFlow<Int>()
launch {
hotFlow.emit(1) // Один раз!
hotFlow.emit(2)
hotFlow.emit(3)
}
launch { hotFlow.collect { println("Collector1: $it") } }
launch { hotFlow.collect { println("Collector2: $it") } }
// Оба получат 2, 3 (1 пропущена — они подписались поздно)
1. StateFlow — Состояние UI
StateFlow хранит одно состояние и эмитит его всем новым подписчикам.
class UserViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun loadUsers() {
viewModelScope.launch {
_uiState.value = UiState.Loading
_uiState.value = UiState.Success(users)
}
}
}
Ключевые свойства:
- Всегда имеет значение (initial state)
- Новые подписчики получают текущее значение
- Не эмитит дубли (value == old)
- Thread-safe
- Идеален для MVVM UI состояния
2. SharedFlow — События
SharedFlow эмитит события без сохранения состояния.
class EventViewModel : ViewModel() {
private val _events = MutableSharedFlow<Event>()
val events: SharedFlow<Event> = _events.asSharedFlow()
fun deleteUser(id: Int) {
viewModelScope.launch {
repository.deleteUser(id)
_events.emit(Event.UserDeleted(id))
}
}
}
class UserFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.events.collect { event ->
when (event) {
is Event.UserDeleted -> navController.popBackStack()
}
}
}
}
}
Параметры:
replay: Сколько прошлых значений переиспользоватьextraBufferCapacity: Буфер для переполненияonBufferOverflow: Поведение при переполнении
3. shareIn() — Преобразование Flow в горячий
Обычный cold Flow можно преобразовать в горячий:
val locationFlow: Flow<Location> = flow {
while (true) {
emit(locationService.getCurrentLocation())
delay(1000)
}
}
val sharedLocation: SharedFlow<Location> = locationFlow
.shareIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
replay = 1
)
// Несколько подписчиков переиспользуют один поток
launch { sharedLocation.collect { updateMap(it) } }
launch { sharedLocation.collect { updateUI(it) } }
4. stateIn() — StateFlow из Flow
Преобразуй Flow в StateFlow:
val userFlow: Flow<User> = userRepository.observeUser(userId)
val userState: StateFlow<User?> = userFlow
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = null
)
val currentUser: User? = userState.value // Быстрый доступ
Сравнение StateFlow, SharedFlow, Flow
| Свойство | StateFlow | SharedFlow | Flow |
|---|---|---|---|
| Горячий/Холодный | Горячий | Горячий | Холодный |
| Хранит состояние | Да (1 значение) | Нет | Нет |
| Новый подписчик получает | Текущее значение | Replayed значения | Ничего |
| Удаляет дубли | Да | Нет | Нет |
| Использование | UI состояние | События | API запросы |
Практические примеры
Поиск пользователей
class SearchViewModel(repo: UserRepository) : ViewModel() {
private val _searchQuery = MutableStateFlow("")
val searchResults: StateFlow<List<User>> = _searchQuery
.debounce(300)
.flatMapLatest { query ->
if (query.isEmpty()) flowOf(emptyList())
else repo.searchUsers(query)
}
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
fun updateQuery(query: String) {
_searchQuery.value = query
}
}
Навигационные события
class LoginViewModel : ViewModel() {
private val _navigationEvents = MutableSharedFlow<NavigationEvent>()
val navigationEvents: SharedFlow<NavigationEvent> = _navigationEvents
fun login(email: String, password: String) {
viewModelScope.launch {
try {
authService.login(email, password)
_navigationEvents.emit(NavigationEvent.GoToHome)
} catch (e: Exception) {
_navigationEvents.emit(NavigationEvent.ShowError(e.message))
}
}
}
}
Best Practices
- StateFlow для UI состояния
- SharedFlow для событий
- Всегда делай flows read-only
- Используй viewModelScope для автоматической очистки
- Обрабатывай ошибки в flow
- Используй debounce() для поиска
- Используй distinctUntilChanged() чтобы не повторяться
Вывод: Горячие потоки — это эволюция LiveData. StateFlow для состояния, SharedFlow для событий, они дают полный контроль над потоком данных в Android приложении.