Comment gérer dans l'architecture MVI les événements qui ne doivent pas être enregistrés ?
sobes.tech IA
Réponse de l'IA
Pour gérer des événements uniques (par exemple, afficher une toast, navigation) dans MVI, qui ne doivent pas survivre à la recréation de la vue ni être stockés dans l'état, différentes approches sont utilisées:
-
Effets secondaires (SingleLiveEvent / Channels):
Mécanisme spécial pour envoyer des événements du ViewModel à la vue.
SingleLiveEvent(dans d'anciens projets ou bibliothèques commeandroidx.lifecycle:lifecycle-livedata-ktx), ouChannelde Flow (dans les projets modernes). Ils garantissent que l'événement ne sera consommé qu'une seule fois.// ViewModel avec Flow et 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() { // Logique métier... viewModelScope.launch { _sideEffect.send(SideEffect.ShowToast("Opération réussie !")) } } } sealed class SideEffect { data class ShowToast(val message: String) : SideEffect() object NavigateNext : SideEffect() } // Dans la vue (Fragment/Activity), observation de 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) { // Implémentation pour afficher la toast } private fun navigateToNextScreen() { // Implémentation pour la navigation } } -
Événements à usage unique dans l'état:
Option moins recommandée, mais parfois utilisée dans des cas simples. Ajoutez un drapeau ou un champ dans l'état
UiStatelui-même, indiquant la présence d'un événement à usage unique. Après traitement de l'événement dans la vue, la vue envoie un nouvelIntentau ViewModel pour réinitialiser ce drapeau ou ce champ dans l'état. Cela peut compliquer la logique et potentiellement entraîner une réactivation en cas de mauvaise gestion ou de recréation de la vue.// Exemple d'état avec drapeau à usage unique data class MyUiState( val data: List<Item> = emptyList(), val isLoading: Boolean = false, val showSuccessToast: Boolean = false // Drapeau pour événement à usage unique ) // Dans le ViewModel, après une opération réussie fun processSuccess() { _uiState.update { it.copy(showSuccessToast = true) } } // Dans la vue, lors du traitement de showSuccessToast = true if (state.showSuccessToast) { showToast("Succès !") // Important : envoyer un Intent pour réinitialiser le drapeau viewModel.onToastShown() } // Dans le ViewModel, pour réinitialiser le drapeau fun onToastShown() { _uiState.update { it.copy(showSuccessToast = false) } }
L'approche la plus recommandée dans Android moderne avec Flow et MVI est d'utiliser Effets secondaires via Channel. C'est une solution propre, sûre et prévisible pour les événements à usage unique.