Как будут вести себя StateFlow и SharedFlow при изменении конфигурации
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
StateFlow и SharedFlow при конфигурационных изменениях
Это критический вопрос про разницу между двумя реактивными потоками в Kotlin. При повороте экрана (конфигурационном изменении) они ведут себя совершенно по-разному.
StateFlow — сохраняет состояние
StateFlow всегда хранит последнее значение состояния и живёт в ViewModel. При конфигурационном изменении Activity пересоздаётся, но ViewModel остаётся:
class UserViewModel : ViewModel() {
// StateFlow живёт в ViewModel, пережидает конфиг изменения
private val _userState = MutableStateFlow<User?>(null)
val userState: StateFlow<User?> = _userState.asStateFlow()
init {
viewModelScope.launch {
api.getUser().collect { user ->
_userState.value = user // Последнее значение сохраняется
}
}
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel: UserViewModel by viewModels()
lifecycleScope.launch {
// StateFlow излучает последнее значение при подписке
// При повороте Activity — получим старое значение сразу
viewModel.userState.collect { user ->
updateUI(user) // user не null (из ViewModel)
}
}
}
}
// Сценарий:
// 1. Первый запуск: user загружается, отображается
// 2. Поворот экрана:
// - Activity пересоздаётся
// - ViewModel остаётся ТЕ ЖЕ
// - StateFlow излучает ПОСЛЕДНЕЕ значение сразу
// - UI обновляется без сетевого запроса
SharedFlow — излучает только новые события
SharedFlow НЕ сохраняет состояние (если не настроен replay). Это просто поток событий. При конфигурационном изменении новый subscriber пропускает все прошлые события:
class EventBus : ViewModel() {
// SharedFlow с replay=0 не сохраняет события
private val _events = MutableSharedFlow<UserEvent>()
val events: SharedFlow<UserEvent> = _events.asSharedFlow()
fun sendEvent(event: UserEvent) {
viewModelScope.launch {
_events.emit(event)
}
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val eventBus: EventBus by viewModels()
lifecycleScope.launch {
eventBus.events.collect { event ->
handleEvent(event) // Событие НЕ будет переподписано
}
}
}
}
// Сценарий:
// 1. Пользователь жмёт кнопку, испускается событие
// 2. Поворот экрана:
// - Activity пересоздаётся
// - Новая подписка на SharedFlow
// - Старое событие ПОТЕРЯНО
// - handleEvent() НЕ вызывается
Визуальное сравнение
| Аспект | StateFlow | SharedFlow |
|---|---|---|
| Сохраняет состояние | Да (последнее значение) | Нет (только события) |
| Что получит новый subscriber | Последнее значение | Ничего (если replay=0) |
| Конфиг изменение | Работает идеально | Можно потерять события |
| Повторная подписка | Получит старое значение | Пропустит старые события |
| Идеальное применение | UI состояние (данные) | События пользователя |
StateFlow с конфиг изменением — правильный способ
class UserViewModel : ViewModel() {
private val _userName = MutableStateFlow<String>("Loading...")
val userName: StateFlow<String> = _userName.asStateFlow()
init {
viewModelScope.launch {
val name = api.getUserName()
_userName.value = name
}
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel: UserViewModel by viewModels()
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.userName.collect { name ->
textView.text = name
}
}
}
}
}
// При повороте:
// 1. ViewModel остаётся (не пересоздаётся)
// 2. StateFlow уже имеет значение
// 3. Новый subscriber получит его сразу
// 4. UI восстанавливается мгновенно
SharedFlow с replay для сохранения событий
Если вам нужно сохранить события в SharedFlow, используйте replay:
// Сохранять последние 3 события
MutableSharedFlow<Event>(replay = 3)
// Или сохранять только последнее значение (как StateFlow)
MutableSharedFlow<Event>(replay = 1)
Практический пример: с repeatOnLifecycle
class MainActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
// repeatOnLifecycle автоматически переподписывается
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.userState.collect { user -> // StateFlow
updateUI(user)
}
}
}
}
}
На собеседовании
Ключевые моменты:
- StateFlow сохраняет состояние — всегда используй для UI данных (пользователь, посты, цены)
- SharedFlow передаёт события — используй для действий (клик кнопки, навигация, уведомления)
- При конфиг изменении StateFlow спасает положение — ViewModel+StateFlow = идеальная архитектура
- SharedFlow требует replay для сохранения — иначе потеряются события при пересоздании
- repeatOnLifecycle — стандартный паттерн для безопасной подписки
Правильный ответ демонстрирует понимание различия между состоянием (StateFlow) и событиями (SharedFlow) в контексте жизненного цикла Android приложения.