Sobes.tech
Middle+

Ako v architektúre MVI spracovať udalosti, ktoré nie je potrebné ukladať?

sobes.tech AI

Odpoveď od AI

Na spracovanie jednorazových udalostí (napríklad zobrazenie toastu, navigácia) v MVI, ktoré by nemali prežiť znovu vytvorenie View alebo byť uložené v stave, sa používajú rôzne prístupy:

  1. Side Effects (SingleLiveEvent / Channels):

    Špeciálny mechanizmus na odosielanie udalostí z ViewModel do View. SingleLiveEvent (v starších projektoch alebo knižniciach ako androidx.lifecycle:lifecycle-livedata-ktx), alebo Channel z Flow (v moderných projektoch). Zaručujú, že udalosť bude spracovaná iba raz.

    // 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() {
            // Biznis logika...
            viewModelScope.launch {
                _sideEffect.send(SideEffect.ShowToast("Operácia bola úspešne dokončená!"))
            }
        }
    }
    
    sealed class SideEffect {
        data class ShowToast(val message: String) : SideEffect()
        object NavigateNext : SideEffect()
    }
    
    // V View (Fragment/Activity) sledujeme 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) {
            // Implementácia zobrazenia toastu
        }
    
        private fun navigateToNextScreen() {
            // Implementácia navigácie
        }
    }
    
  2. Jednorazové udalosti v stave:

    Menej odporúčaný prístup, ale niekedy použitý v jednoduchých prípadoch. Pridáme do UiState príznak alebo pole, ktoré označuje prítomnosť jednorazovej udalosti. Po spracovaní udalosti vo View, View odošle nový Intent do ViewModel, aby resetoval tento príznak/pole v stave. To môže skomplikovať logiku a viesť k opätovnému spusteniu udalosti pri nesprávnom spracovaní alebo znovu vytvorení View.

    // Príklad stavu s jednorazovým príznakom
    data class MyUiState(
        val data: List<Item> = emptyList(),
        val isLoading: Boolean = false,
        val showSuccessToast: Boolean = false // Príznak pre jednorazovú udalosť
    )
    
    // V ViewModel pri úspešnej operácii
    fun processSuccess() {
        _uiState.update { it.copy(showSuccessToast = true) }
    }
    
    // V View pri spracovaní showSuccessToast = true
    if (state.showSuccessToast) {
        showToast("Úspešne!")
        // Dôležité: odoslať Intent na resetovanie príznaku
        viewModel.onToastShown()
    }
    
    // V ViewModel na resetovanie príznaku
    fun onToastShown() {
        _uiState.update { it.copy(showSuccessToast = false) }
    }
    

Najlepší prístup v modernom Android s Flow a MVI je použitie Side Effects cez Channel. Je to čisté, bezpečné a predvídateľné riešenie pre jednorazové udalosti.