Jak v architektuře MVI zpracovávat události, které není třeba ukládat?
sobes.tech AI
Odpověď od AI
Pro zpracování jednorázových událostí (například zobrazení toastu, navigace) v MVI, které by neměly přežít znovuvytvoření View nebo být uloženy do stavu, se používají různé přístupy:
-
Side Effects (SingleLiveEvent / Channels):
Speciální mechanismus pro odesílání událostí z ViewModel do View.
SingleLiveEvent(v starších projektech nebo knihovnách jakoandroidx.lifecycle:lifecycle-livedata-ktx), neboChannelz Flow (v moderních projektech). Zaručují, že událost bude zpracována pouze jednou.// ViewModel s Flow a 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() { // Business logika... viewModelScope.launch { _sideEffect.send(SideEffect.ShowToast("Operace byla úspěšně dokončena!")) } } } sealed class SideEffect { data class ShowToast(val message: String) : SideEffect() object NavigateNext : SideEffect() } // V View (Fragment/Activity) posloucháme 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) { // Implementace zobrazení toastu } private fun navigateToNextScreen() { // Implementace navigace } } -
Jednorázové události ve stavu:
Méně doporučený přístup, ale někdy použitelný v jednoduchých případech. Přidáme do
UiStatepříznak nebo pole, které označuje přítomnost jednorázové události. Po zpracování události ve View, View odešle novýIntentViewModelu, aby resetoval tento příznak/pole ve stavu. To může zkomplikovat logiku a vést k opětovnému spuštění události při nesprávném zpracování nebo znovuvytvoření View.// Příklad stavu s jednorázovým příznakem data class MyUiState( val data: List<Item> = emptyList(), val isLoading: Boolean = false, val showSuccessToast: Boolean = false // Příznak pro jednorázovou událost ) // Ve ViewModelu při úspěšné operaci fun processSuccess() { _uiState.update { it.copy(showSuccessToast = true) } } // Ve View při zpracování showSuccessToast = true if (state.showSuccessToast) { showToast("Úspěšně!") // Důležité: odeslat Intent pro resetování příznaku viewModel.onToastShown() } // Ve ViewModelu pro resetování příznaku fun onToastShown() { _uiState.update { it.copy(showSuccessToast = false) } }
Nejlepší přístup v moderním Android s Flow a MVI je použití Side Effects přes Channel. Je to čisté, bezpečné a předvídatelné řešení pro jednorázové události.