1

我正在使用 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 并再次打开片段。

我已经检查了这个问题并尝试了上述链接中提出的所有解决方案(如您在注释代码中所见)。只有这个解决方案有效,但它不是很优雅,并迫使我每次都进行检查。

我想尝试一个更可靠和最优的解决方案。

请记住,在该线程中没有生命周期实现。

4

3 回答 3

1

这就是LiveData工作原理,它是一个价值持有者,它拥有最后一个价值。

如果您需要消耗您的对象,以便该操作仅触发一次,请考虑将您的对象包装在 a 中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()
            }
        }
}
于 2020-05-19T20:30:50.207 回答
1

之所以会出现此问题,是因为如果有任何数据随时可用,LiveData 总是将可用数据发布给观察者。之后它将发布更新。我认为这是预期的工作,因为即使在问题跟踪器中提出了错误,这种行为也没有得到修复。但是,SO 中的开发人员建议了许多解决方案,我发现这个很容易适应并且实际上工作得很好。

解决方案

viewModel.messagesLiveData.observe(viewLifecycleOwner, {
        if (viewLifecycleOwner.lifecycle.currentState == Lifecycle.State.RESUMED) {
            //Do your stuff
        }
    })  
于 2021-11-15T13:34:25.013 回答
0

更新

找到了弗朗西斯在这里回答的不同且仍然有用的实现。看一看

于 2020-05-20T09:45:42.613 回答