簡體   English   中英

Kotlin ViewModel onchange 從片段返回時被多次調用(使用生命周期實現)

[英]Kotlin ViewModel onchange gets called multiple times when back from Fragment (using Lifecycle implementation)

我正在使用 MVVM 架構。

編碼

當我單擊一個按鈕時,會觸發orderAction方法。 它只是發布一個枚舉(將添加進一步的邏輯)。

視圖模型

class DashboardUserViewModel(application: Application) : SessionViewModel(application) {

    enum class Action {
        QRCODE,
        ORDER,
        TOILETTE
    }

    val action: LiveData<Action>
        get() = mutableAction
    private val mutableAction = MutableLiveData<Action>()

    init {
    }

    fun orderAction() {
        viewModelScope.launch(Dispatchers.IO) {
            // Some queries before the postValue
            mutableAction.postValue(Action.QRCODE)    
        }
    }
}

片段觀察 LiveData obj 並調用打開新片段的方法。 我在這里使用導航器,但我認為有關它的詳細信息在這種情況下沒有用處。 請注意,我正在使用viewLifecycleOwner

分段

class DashboardFragment : Fragment() {

    lateinit var binding: FragmentDashboardBinding
    private val viewModel: DashboardUserViewModel by lazy {
        ViewModelProvider(this).get(DashboardUserViewModel::class.java)
    }

    private val observer = Observer<DashboardUserViewModel.Action> {
        // Tried but I would like to have a more elegant solution
        //if (viewLifecycleOwner.lifecycle.currentState == Lifecycle.State.RESUMED)
            it?.let {
                when (it) {
                    DashboardUserViewModel.Action.QRCODE -> navigateToQRScanner()
                    DashboardUserViewModel.Action.ORDER -> TODO()
                    DashboardUserViewModel.Action.TOILETTE -> TODO()
                }
            }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentDashboardBinding.inflate(inflater, container, false)
        binding.viewModel = viewModel
        binding.lifecycleOwner = this

        viewModel.action.observe(viewLifecycleOwner, observer)

        // Tried but still having the issue
        //viewModel.action.reObserve(viewLifecycleOwner, observer)

        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        // Tried but still having the issue
        //viewModel.action.removeObserver(observer)
    }

    private fun navigateToQRScanner() {
        log("START QR SCANNER")
        findNavController().navigate(LoginFragmentDirections.actionLoginToPrivacy())
    }
}

問題

當我關閉打開的片段(使用 findNavController().navigateUp())時,會立即調用 DashboardFragment 的 Observe.onChanged 並再次打開片段。

我已經檢查了這個問題並嘗試了上述鏈接中的所有建議解決方案(如您在注釋代碼中所見)。 只有這個解決方案有效,但它不是很優雅,並迫使我每次都進行檢查。

我想嘗試一個更可靠和最優的解決方案。

請記住,在該線程中沒有生命周期實現。

這就是LiveData的工作方式,它是一個價值持有者,它持有最后一個價值。

如果您需要消耗您的對象,以便該操作僅觸發一次,請考慮將 object 包裝在Consumable中,如下所示

class ConsumableValue<T>(private val data: T) {

    private val consumed = AtomicBoolean(false)

    fun consume(block: ConsumableValue<T>.(T) -> Unit) {
        if (!consumed.getAndSet(true)) {
            block(data)
        }
    }
}

然后您將 LiveData 定義為

val action: LiveData<ConsumableValue<Action>>
    get() = mutableAction
private val mutableAction = MutableLiveData<ConsumableValue<Action>>()

然后在你的觀察者中,你會做

private val observer = Observer<ConsumableValue<DashboardUserViewModel.Action>> {
        it?.consume { action ->
            when (action) {
                DashboardUserViewModel.Action.QRCODE -> navigateToQRScanner()
                DashboardUserViewModel.Action.ORDER -> TODO()
                DashboardUserViewModel.Action.TOILETTE -> TODO()
            }
        }
}

更新

找到了弗朗西斯在這里回答的不同且仍然有用的實現。 看一看

之所以會出現此問題,是因為如果有任何數據隨時可用,LiveData 總是將可用數據發布給觀察者。 之后它將發布更新。 我認為這是預期的工作,因為即使在問題跟蹤器中提出了錯誤,這種行為也沒有得到修復。 但是,SO 中的開發人員建議了許多解決方案,我發現這個很容易適應並且實際上工作得很好。

解決方案

viewModel.messagesLiveData.observe(viewLifecycleOwner, {
        if (viewLifecycleOwner.lifecycle.currentState == Lifecycle.State.RESUMED) {
            //Do your stuff
        }
    })  

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM