Sobes.tech
Middle+

Kā MVI arhitektūrā apstrādāt notikumus, kurus nav nepieciešams saglabāt?

sobes.tech AI

Atbilde no AI

Vienreizēju notikumu (piemēram, tosta rādīšana, navigācija), kas nedrīkst pārdzīvot View pārveidošanu vai tikt saglabāti stāvoklī, apstrādei MVI tiek izmantoti dažādi pieejas veidi:

  1. Side Effects (SingleLiveEvent / Channels):

    Īpašs mehānisms, kas nosūta notikumus no ViewModel uz View: SingleLiveEvent (vecākos projektos vai androidx.lifecycle:lifecycle-livedata-ktx bibliotēkās), vai Channel no Flow. Tie garantē, ka notikums tiks apstrādāts tikai vienu reizi:

    // ViewModel ar Flow un 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() {
            // Biznesa loģika...
            viewModelScope.launch {
                _sideEffect.send(SideEffect.ShowToast("Operācija veiksmīgi pabeigta!"))
            }
        }
    }
    
    sealed class SideEffect {
        data class ShowToast(val message: String) : SideEffect()
        object NavigateNext : SideEffect()
    }
    
    // Skats (Fragment/Activity) novēro 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) {
            // Tosta rādīšana
        }
    
        private fun navigateToNextScreen() {
            // Navigācija
        }
    }
    
  2. Vienreizēji notikumi stāvoklī:

    Mazāk ieteicams, bet dažreiz izmanto vienkāršiem gadījumiem. Piešķiram zīmi vai lauku UiState, kas norāda uz vienreizēju notikumu. Pēc notikuma apstrādes View, ViewModel saņem jaunu Intent, lai atiestatītu šo zīmi/lauku. Tas var sarežģīt loģiku un izraisīt atkārtotu izpildi kļūdas vai pārveidošanas laikā:

    // Piemērs ar stāvokli un vienreizēju zīmi
    data class MyUiState(
        val data: List<Item> = emptyList(),
        val isLoading: Boolean = false,
        val showSuccessToast: Boolean = false // Zīme vienreizējam notikumam
    )
    
    // ViewModel'ā pēc veiksmīga procesa
    fun processSuccess() {
        _uiState.update { it.copy(showSuccessToast = true) }
    }
    
    // View'ā, ja showSuccessToast = true
    if (state.showSuccessToast) {
        showToast("Veiksmīgi!")
        // Svarīgi: atiestatīt zīmi, nosūtot Intent
        viewModel.onToastShown()
    }
    
    // ViewModel'ā zīmes atiestatīšana
    fun onToastShown() {
        _uiState.update { it.copy(showSuccessToast = false) }
    }
    

Mūsdienīgs Android ar Flow un MVI labākais veids — izmantot Side Effects caur Channel. Tas ir tīrs, drošs un prognozējams risinājums vienreizējiem notikumiem.