从 LiveData 观察者调用时,导航组件的默认后台堆栈不起作用

Has*_*tty 3 android kotlin android-jetpack android-architecture-navigation

我正在使用带有导航抽屉的 Android 导航组件(如在 Android Studio 模板中)。我将片段 A、B、C 作为顶级片段,用于导航抽屉中,片段 Z 与导航图中的片段 A 相连。现在我在片段 A 中有一个按钮。单击该按钮将使用安全参数打开片段 Z。

    binding.button.setOnClickListener {
        val action = NewsFragmentDirections.actionNavNewsToNewsDetailsFragment()
        it.findNavController().navigate(action)
    }
Run Code Online (Sandbox Code Playgroud)

打开片段 Z 时,应用栏图标会自动变为后退按钮,这将允许我返回片段 A。

这些工作正常,但这个问题是,当我在实时数据 obsedrver 中使用相同的安全参数代码时,后退按钮不起作用。

    viewModel.actionNewsDetails.observe(viewLifecycleOwner, {
        val action = NewsFragmentDirections.actionNavNewsToNewsDetailsFragment()
        findNavController().navigate(action)
    })
Run Code Online (Sandbox Code Playgroud)

这里有一些额外的细节

  • 一旦我们在片段 Z 中,它会像往常一样显示返回导航,但只是单击它不会执行任何操作
  • 当我快速点击后退按钮几次时,我注意到应用栏标题闪烁(在片段 A 和 Z 之间变化)
  • 当我在片段 Z 中时,我可以通过滑动打开导航抽屉
  • 我的实时数据代码写在片段 A 的 onCreateView()
  • 实时数据由 ViewModel 中的函数触发

我一直在努力解决这个问题。对不起,我的英语不好。

Bog*_*oid 6

接下来的信息非常重要:

当我快速点击后退按钮几次时,我注意到应用栏标题闪烁(在片段 A 和 Z 之间变化)

我很确定会发生什么,你在片段 Z 中的后退按钮工作正常,你的片段 A 显示,它的 liveData 再次被触发并再次导航到片段 Z。这发生得非常快,但正如你所指出的,当你这样做时非常快,您可以看到延迟。

解决方案:在您的 LiveData 观察器中导航到片段 Z 之前,更改 liveData 的值,以便当您返回片段 A 时,它不会再次触发。

几周前,这个问题让我损失了一个小时。

26/10/2020 编辑

要解决该问题,请实现SingleLiveEvent该类并使用它代替MutableLiveData.

SingleLiveEvent.class

/**
 * A lifecycle-aware observable that sends only new updates after subscription, used for events like
 * navigation and Snackbar messages.
 * <p>
 * This avoids a common problem with events: on configuration change (like rotation) an update
 * can be emitted if the observer is active. This LiveData only calls the observable if there's an
 * explicit call to setValue() or call().
 * <p>
 * Note that only one observer is going to be notified of changes.
 */
public class SingleLiveEvent<T> extends MutableLiveData<T> {

    private static final String TAG = "SingleLiveEvent";

    private final AtomicBoolean mPending = new AtomicBoolean(false);

    @MainThread
    public void observe(LifecycleOwner owner, final Observer<? super T> observer) {

        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
        }

        // Observe the internal MutableLiveData
        super.observe(owner, new Observer<T>() {
            @Override
            public void onChanged(@Nullable T t) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t);
                }
            }
        });
    }

    @MainThread
    public void setValue(@Nullable T t) {
        mPending.set(true);
        super.setValue(t);
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    public void call() {
        setValue(null);
    }
}
Run Code Online (Sandbox Code Playgroud)

科特林版

class SingleLiveEvent<T> : MutableLiveData<T>() {
    val TAG: String = "SingleLiveEvent"

    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, Observer<T> { t ->
            if (mPending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

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

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() {
        value = null
    }
}
Run Code Online (Sandbox Code Playgroud)