Sobes.tech
Middle+

Cum se gestionează în arhitectura MVI evenimentele care nu trebuie să fie salvate?

sobes.tech AI

Răspuns de la AI

Pentru gestionarea evenimentelor unice (de exemplu, afișarea unui toast, navigare) în MVI, care nu trebuie să supraviețuiască recreării View sau să fie stocate în stare, se folosesc abordări diferite:

  1. Side Effects (SingleLiveEvent / Channels):

    Un mecanism special pentru trimiterea evenimentelor din ViewModel către View. SingleLiveEvent (în proiecte mai vechi sau biblioteci precum androidx.lifecycle:lifecycle-livedata-ktx), sau Channel din Flow (în proiecte moderne). Acestea garantează că evenimentul va fi consumat o singură dată.

    // ViewModel cu Flow și 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() {
            // Logică de business...
            viewModelScope.launch {
                _sideEffect.send(SideEffect.ShowToast("Operațiunea a fost finalizată cu succes!"))
            }
        }
    }
    
    sealed class SideEffect {
        data class ShowToast(val message: String) : SideEffect()
        object NavigateNext : SideEffect()
    }
    
    // În View (Fragment/Activity), ascultăm pentru 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) {
            // Implementarea afișării toast-ului
        }
    
        private fun navigateToNextScreen() {
            // Implementarea navigării
        }
    }
    
  2. Evenimente de utilizare unică în stare:

    O variantă mai puțin recomandată, dar uneori utilizată în cazuri simple. Se adaugă un flag sau un câmp în starea UiState care indică prezența unui eveniment unicat. După procesarea evenimentului în View, View trimite un nou Intent către ViewModel pentru a reseta acest flag/câmp în stare. Aceasta poate complica logica și poate duce la reapariția evenimentului în caz de re-creare sau gestionare greșită.

    // Exemplu de stare cu flag unicat
    data class MyUiState(
        val data: List<Item> = emptyList(),
        val isLoading: Boolean = false,
        val showSuccessToast: Boolean = false // Flag pentru eveniment unicat
    )
    
    // În ViewModel, la succesul unei operații
    fun processSuccess() {
        _uiState.update { it.copy(showSuccessToast = true) }
    }
    
    // În View, la procesarea showSuccessToast = true
    if (state.showSuccessToast) {
        showToast("Succes!")
        // Este important: trimiteți un Intent pentru resetarea flag-ului
        viewModel.onToastShown()
    }
    
    // În ViewModel, pentru resetarea flag-ului
    fun onToastShown() {
        _uiState.update { it.copy(showSuccessToast = false) }
    }
    

Cel mai recomandat abordare în Android modern cu Flow și MVI - utilizarea Side Effects prin Channel. Este o soluție curată, sigură și previzibilă pentru evenimente unice.