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

Android Navigation component graph stop working properly when I use navController in LiveData observer

我在我的演示应用程序中使用 android 导航组件。我有一个非常简单的案例。一个 activity,两个片段,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 一起使用?

发生此崩溃是因为当您单击后退按钮时,您的视图模型 openFragmentB 观察器再次收到通知,并且它正在尝试使用操作 openFragmentB 导航到 Fragment B,但此时 NavController 当前目的地仍然是 Fragment B,并且片段 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

感谢@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"
    }
}