Sobes.tech
Middle+

როგორ უნდა მოვიქცეთ MVI არქიტექტურაში იმ მოვლენებთან, რომლებიც შენახვას არ საჭიროებს?

sobes.tech AI

პასუხი AI-სგან

მხოლოდ ერთხელ მომხდარი მოვლენების (მაგალითად, ტოსტის ჩვენება, ნავიგაცია) დამუშავებისთვის 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) {
            // ტოსტის ჩვენების რეალიზაცია
        }
    
        private fun navigateToNextScreen() {
            // ნავიგაციის რეალიზაცია
        }
    }
    
  2. ერთი გამოყენების მოვლენები მდგომარეობაში:

    ნაკლებად სასურველი ვარიანტია, მაგრამ ზოგჯერ გამოიყენება მარტივ შემთხვევებში. ვამატებთ ნიშანს ან ველს UiState-ში, რომელიც მიუთითებს ერთჯერად მოვლენაზე. მოვლენას დამუშავების შემდეგ, View-დან ViewModel-ს გადაეცემა ახალი Intent, რათა ამ ნიშანს/ველს წაშალოს. ეს შეიძლება გაართულოს ლოგიკა და გამოიწვიოს განმეორებითი გაშვება შეცდომით ან View-ის გადაკეთების დროს:

    // მაგალითი State-ით, სადაც არის ერთჯერადი ნიშანი
    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-ის გამოყენებით, საუკეთესო მიდგომაა — გამოყენება Side Effects Channel-ით. ეს სუფთა, უსაფრთხო და პროგნოზირებადი გადაწყვეტილებაა ერთჯერადი მოვლენებისთვის.