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

Как будут вести себя StateFlow и SharedFlow при изменении конфигурации

2.0 Middle🔥 161 комментариев
#Жизненный цикл и навигация#Многопоточность и асинхронность

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

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

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() НЕ вызывается

Визуальное сравнение

АспектStateFlowSharedFlow
Сохраняет состояниеДа (последнее значение)Нет (только события)
Что получит новый 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)
                }
            }
        }
    }
}

На собеседовании

Ключевые моменты:

  1. StateFlow сохраняет состояние — всегда используй для UI данных (пользователь, посты, цены)
  2. SharedFlow передаёт события — используй для действий (клик кнопки, навигация, уведомления)
  3. При конфиг изменении StateFlow спасает положение — ViewModel+StateFlow = идеальная архитектура
  4. SharedFlow требует replay для сохранения — иначе потеряются события при пересоздании
  5. repeatOnLifecycle — стандартный паттерн для безопасной подписки

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