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

Navigation Component default backstack not working when called from a LiveData observer

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

    binding.button.setOnClickListener {
        val action = NewsFragmentDirections.actionNavNewsToNewsDetailsFragment()
        it.findNavController().navigate(action)
    }

当片段 Z 打开时,应用栏图标会自动变为后退按钮,这样我就可以返回到片段 A。

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

    viewModel.actionNewsDetails.observe(viewLifecycleOwner, {
        val action = NewsFragmentDirections.actionNavNewsToNewsDetailsFragment()
        findNavController().navigate(action)
    })

这里有一些额外的细节

我一直在为这个问题苦苦挣扎。抱歉我的英语不好。

接下来的信息很重要:

When I clicked the back button fast for several time, I noticed the app bar title flickering (changing between fragment A and Z)

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

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

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

编辑 26/10/2020

要解决该问题,请实施 SingleLiveEvent class 并使用它代替 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);
    }
}

Kotlin 版本

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
    }
}