Sobes.tech
Middle+

Kuidas MVI arhitektuuris käsitleb sündmusi, mida ei ole vaja salvestada?

sobes.tech AI

Vastus AI-lt

Ühekordsed sündmused (näiteks tostide kuvamine, navigatsioon), mis ei tohiks üle elada View uuesti loomist või salvestada olekusse, töödeldakse MVI-s erinevate lähenemisviisidega:

  1. Side Effects (SingleLiveEvent / Channels):

    Spetsiaalne mehhanism sündmuste saatmiseks ViewModelist View'ile: SingleLiveEvent (vanemates projektides või androidx.lifecycle:lifecycle-livedata-ktx teekides), või Channel Flow'ist. Need tagavad, et sündmus käsitletakse ainult üks kord:

    // ViewModel Flow ja Channel'iga
    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() {
            // Äri-logika...
            viewModelScope.launch {
                _sideEffect.send(SideEffect.ShowToast("Tegevus edukalt lõpetatud!"))
            }
        }
    }
    
    sealed class SideEffect {
        data class ShowToast(val message: String) : SideEffect()
        object NavigateNext : SideEffect()
    }
    
    // Vaates (Fragment/Activity) jälgitakse SideEffect'i
    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) {
            // Tosti kuvamine
        }
    
        private fun navigateToNextScreen() {
            // Navigatsioon
        }
    }
    
  2. Ühekordsed sündmused olekus:

    Vähem soovitatav lähenemisviis, kuid mõnikord kasutatakse lihtsates olukordades. Lisame UiState-i märgi või välja, mis näitab ühekordset sündmust. Pärast sündmuse töötlemist View's, saadab ViewModel uue Intent, et nullida see märgistus/väli. See võib keeruliseks muuta loogikat ja põhjustada korduvat käivitamist valesti töötlemise või uuesti loomise tõttu:

    // Näide olekuga ja ühekordse märgisega
    data class MyUiState(
        val data: List<Item> = emptyList(),
        val isLoading: Boolean = false,
        val showSuccessToast: Boolean = false // Märk ühekordseks sündmuseks
    )
    
    // ViewModel'is pärast edukat toimingut
    fun processSuccess() {
        _uiState.update { it.copy(showSuccessToast = true) }
    }
    
    // View's, kui showSuccessToast = true
    if (state.showSuccessToast) {
        showToast("Edukas!")
        // Oluline: märgi nullimiseks saada Intent
        viewModel.onToastShown()
    }
    
    // ViewModel'is märgi nullimine
    fun onToastShown() {
        _uiState.update { it.copy(showSuccessToast = false) }
    }
    

Kaasaegne Android Flow ja MVI puhul on parim lähenemine — kasutada Side Effects Channel kaudu. See on puhas, turvaline ja ennustatav lahendus ühekordsete sündmuste jaoks.