Sobes.tech
Middle+

Πώς διαχειριζόμαστε στην αρχιτεκτονική MVI τα γεγονότα που δεν χρειάζεται να αποθηκευτούν;

sobes.tech AI

Απάντηση από AI

Για την επεξεργασία μοναδικών γεγονότων (π.χ. εμφάνιση toast, πλοήγηση) στο MVI, που δεν πρέπει να επιβιώσουν από την αναδημιουργία της View ή να αποθηκευτούν στην κατάσταση, χρησιμοποιούνται διαφορετικές προσεγγίσεις:

  1. Side Effects (SingleLiveEvent / Channels):

    Μηχανισμός ειδικά για την αποστολή γεγονότων από το ViewModel στο View. SingleLiveEvent (σε παλαιότερα έργα ή βιβλιοθήκες όπως το androidx.lifecycle:lifecycle-livedata-ktx), ή Channel από το Flow (σε σύγχρονα έργα). Διασφαλίζουν ότι το γεγονός θα καταναλωθεί μόνο μία φορά.

    // ViewModel με Flow και 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() {
            // Επιχειρηματική λογική...
            viewModelScope.launch {
                _sideEffect.send(SideEffect.ShowToast("Η λειτουργία ολοκληρώθηκε με επιτυχία!"))
            }
        }
    }
    
    sealed class SideEffect {
        data class ShowToast(val message: String) : SideEffect()
        object NavigateNext : SideEffect()
    }
    
    // Στο View (Fragment/Activity), ακούμε για 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) {
            // Υλοποίηση εμφάνισης toast
        }
    
        private fun navigateToNextScreen() {
            // Υλοποίηση πλοήγησης
        }
    }
    
  2. Μοναδικά γεγονότα σε κατάσταση:

    Λιγότερο προτεινόμενη προσέγγιση, αλλά μερικές φορές χρησιμοποιείται σε απλές περιπτώσεις. Προσθέτουμε μια σημαία ή πεδίο στην ίδια την κατάσταση UiState, που υποδεικνύει την ύπαρξη ενός μοναδικού γεγονότος. Μετά την επεξεργασία του γεγονότος στο View, το View στέλνει ένα νέο Intent στο ViewModel για να μηδενίσει αυτή τη σημαία/πεδίο στην κατάσταση. Αυτό μπορεί να περιπλέξει τη λογική και να οδηγήσει σε επανεμφάνιση του γεγονότος σε περίπτωση λανθασμένης διαχείρισης ή αναδημιουργίας της View.

    // Παράδειγμα κατάστασης με σημαία μοναδικού γεγονότος
    data class MyUiState(
        val data: List<Item> = emptyList(),
        val isLoading: Boolean = false,
        val showSuccessToast: Boolean = false // Σημαία για μοναδικό γεγονός
    )
    
    // Στο ViewModel, κατά την επιτυχή λειτουργία
    fun processSuccess() {
        _uiState.update { it.copy(showSuccessToast = true) }
    }
    
    // Στο View, κατά την επεξεργασία του showSuccessToast = true
    if (state.showSuccessToast) {
        showToast("Επιτυχία!")
        // Σημαντικό: αποστολή Intent για μηδενισμό της σημαίας
        viewModel.onToastShown()
    }
    
    // Στο ViewModel, για μηδενισμό της σημαίας
    fun onToastShown() {
        _uiState.update { it.copy(showSuccessToast = false) }
    }
    

Η πιο συνιστώμενη προσέγγιση στο σύγχρονο Android με Flow και MVI είναι η χρήση Side Effects μέσω Channel. Είναι μια καθαρή, ασφαλής και προβλέψιμη λύση για μοναδικά γεγονότα.