当我在 LiveData 观察者中使用 navController 时,Android Navigation 组件图停止正常工作

Posted

技术标签:

【中文标题】当我在 LiveData 观察者中使用 navController 时,Android Navigation 组件图停止正常工作【英文标题】:Android Navigation component graph stop working properly when I use navController in LiveData observer 【发布时间】:2019-05-03 16:43:49 【问题描述】:

我在我的演示应用程序中使用了 android 导航组件。我有一个非常简单的案例。一个活动,两个片段,A 和 B。我已经设置了导航控件,就像 Google 的示例应用程序一样。当我尝试使用简单的 onClickListener 从 A 打开片段 B 时,如下所示:

val button.setOnClickListener 
       val action = AFragmentDirections.openFragmentB()
       findNavController().navigate(action)
    

一切正常。 B 片段打开,并通过点击后退按钮弹出。 但是当我尝试从 LiveData 观察者中使用它时,就像这样:

viewModel.openFragmentB.observe(viewLifecycleOwner, Observer 
        val action = AFragmentDirections.openFragmentB()
        findNavController().navigate(action)
    )

片段 B 打开,但通过点击后退按钮应用程序崩溃并出现错误 导航目的地 com.myapp:id/open_fragmetn_b 对此 NavController 是未知的。

为什么会发生这种情况以及如何将导航组件与 LiveData 一起使用?

【问题讨论】:

【参考方案1】:

发生此崩溃是因为当您单击后退按钮时,您的 viewmodel openFragmentB 观察者会再次收到通知,并且它正在尝试使用操作 openFragmentB 导航到 Fragment B,但此时 NavController 当前目标仍然是 Fragment B,并且 Fragment B没有动作 openFragmentB。

对此有多种解决方案,最简单的一种是在观察者内部添加检查值是否不为空,最后将 openFragmentB 值设置为空:

if(it!=null) 
    val action = AFragmentDirections.openFragmentB()
    findNavController().navigate(action)
    viewModel.openFragmentB.value=null

但为了获得更好的方法,您可以阅读有关 SingleLIveEvent 的信息: https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150

【讨论】:

我已经在使用 LiveEvent,但由于某种原因,我使用 livedata 进行导航,但有一天我被卡住了。你的帖子和这篇文章对我帮助很大。谢谢! 啊...我终于明白为什么子片段的navigateUp在循环...使用单事件方法解决了它。【参考方案2】:

感谢@Alex 的回答,我创建了您提到的相同类,并且只添加了初始可选构造函数,如下所示:

import android.util.Log
import androidx.annotation.MainThread
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean

class SingleLiveEvent<T>(initialVal: T? = null) : MutableLiveData<T?>() 

    init 
        if(initialVal != null)
            super.setValue(initialVal)
    

    private val mPending = AtomicBoolean(false)
    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<in T?>) 
        if (hasActiveObservers()) 
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
        

        // Observe the internal MutableLiveData
        super.observe(owner,  t ->
            if (mPending.compareAndSet(true, false)) 
                observer.onChanged(t)
            
        )
    

    @MainThread
    override fun setValue(t: T?) 
        mPending.set(true)
        super.setValue(t)
    

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() 
        value = null
    

    companion object 
        private const val TAG = "SingleLiveEvent"
    

【讨论】:

以上是关于当我在 LiveData 观察者中使用 navController 时,Android Navigation 组件图停止正常工作的主要内容,如果未能解决你的问题,请参考以下文章

Android Room LiveData观察器未更新

如何在 MVVM 架构中观察 RecyclerView 适配器中的 LiveData?

在 xml 中设置/观察 livedata 的正确方法是啥?调用观察者方法失败

如何在MVVM架构中观察RecyclerView适配器中的LiveData?

从没有中间转换变量的片段中观察 ViewModel LiveData

jetpack-liveData 原理解析