Как в архитектуре MVI обрабатывать события, которые не требуется сохранять?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
Для обработки одноразовых событий (например, отображение тоста, навигация) в MVI, которые не должны переживать пересоздание View или сохраняться в состоянии, используются различные подходы:
-
Side Effects (SingleLiveEvent / Channels):
Специальный механизм для отправки событий из ViewModel во View.
SingleLiveEvent(в старых проектах или библиотеках типаandroidx.lifecycle:lifecycle-livedata-ktx), илиChannelиз Flow (в современных проектах). Они гарантируют, что событие будет потреблено только один раз.// ViewModel с Flow и Channel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.receiveAsFlow import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch class MyViewModel : ViewModel() { private val _sideEffect = Channel<SideEffect>(Channel.BUFFERED) val sideEffect = _sideEffect.receiveAsFlow() fun doSomething() { // Бизнес-логика... viewModelScope.launch { _sideEffect.send(SideEffect.ShowToast("Операция выполнена успешно!")) } } } sealed class SideEffect { data class ShowToast(val message: String) : SideEffect() object NavigateNext : SideEffect() } // В View (Fragment/Activity) наблюдаем за SideEffect import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch class MyFragment : Fragment() { private val viewModel: MyViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewLifecycleOwner.lifecycleScope.launch { viewModel.sideEffect.collect { effect -> when (effect) { is SideEffect.ShowToast -> showToast(effect.message) SideEffect.NavigateNext -> navigateToNextScreen() } } } } private fun showToast(message: String) { // Реализация отображения тоста } private fun navigateToNextScreen() { // Реализация навигации } } -
Single Use Events в State:
Менее предпочтительный вариант, но иногда используется в простых случаях. Добавляем флаг или поле в само состояние
UiState, которое указывает на наличие одноразового события. После обработки события во View, View отправляет новыйIntentViewModel'и, чтобы сбросить этот флаг/поле в состоянии. Это может усложнить логику и потенциально приводить к повторному срабатыванию при неправильной обработке или пересоздании View.// Пример State с одноразовым флагом data class MyUiState( val data: List<Item> = emptyList(), val isLoading: Boolean = false, val showSuccessToast: Boolean = false // Флаг для одноразового события ) // В ViewModel при успешной операции fun processSuccess() { _uiState.update { it.copy(showSuccessToast = true) } } // В View при обработке showSuccessToast = true if (state.showSuccessToast) { showToast("Успешно!") // Важно: отправить Intent для сброса флага viewModel.onToastShown() } // В ViewModel для сброса флага fun onToastShown() { _uiState.update { it.copy(showSuccessToast = false) } }
Наиболее recommended подход в современном Android с Flow и MVI - использование Side Effects через Channel. Это чистое, безопасное и предсказуемое решение для одноразовых событий.