android - 为什么 LiveData 值需要包装器?

标签 android kotlin mvvm observable android-livedata

我正在查看 JetSurvey project (Android Jetpack Compose 示例项目)并注意到他们创建了一个类来将 LiveData 值包装在他们的 ViewModel 类中。这是我正在谈论的类(class):

/**
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
data class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

在 ViewModel 中,它是这样使用的:

private val _navigateTo = MutableLiveData<Event<Screen>>()
    val navigateTo: LiveData<Event<Screen>> = _navigateTo

fun signInAsGuest() {
        _navigateTo.value = Event(Survey)
    }

似乎 Event 类的意义在于避免导航发生多次。但是,我一开始不明白这是怎么发生的,因为只有在 LiveData 值更新后才会触发导航。每次更新值时,都会创建一个新的 Event 对象,因此它会再次运行。

那么在 Fragment 中,viewModel.navigateTo.observe(viewLifecycleOwner) 中的代码是否有可能在值未更新的情况下运行多次?如果是这样,会在什么情况下发生?

如果我对事件包装器的作用的理解不正确,那么它的实现目的是什么?有必要吗?

最佳答案

假设 fragment 中的 LiveData 观察器导航到第二个 fragment 。用户旋转屏幕,以便销毁第一个 Fragment 实例。当他们退出第二个 Fragment 时,将重新创建第一个 Fragment 的新实例,因此再次触发其观察者。如果没有事件包装器,它会突然而令人惊讶地立即导航回第二个 fragment 。

此外,一个 LiveData 可能有多个观察者。也许两个不​​同的 Fragment 正在观察相同的事件 LiveData,但您不想冒险为同一事件向用户显示两次消息。例如,他们可以导航到第二个 Fragment,该 Fragment 正在观察与第一个 Fragment 相同的事件 LiveData。一个事件触发,第二个 Fragment 观察器向用户显示一个对话框或其他东西。然后用户返回到第一个 fragment ,同一个事件将触发第一个 fragment 的观察者,因此事件被处理两次。

如果您的项目使用协程,则针对此事件观察问题的更简洁的解决方案是使用 replay 为 0 的 SharedFlow,而不是使用带有事件包装器类的 LiveData。我怀疑他们没有在 JetSurvey 项目中使用 SharedFlow 的原因是他们不想假设您已经熟悉 Flows,而这不是示例的内容。

一个名为 SingleLiveEvent 的替代解决方案曾出现在官方 Android 示例中,but was considered too hacky to add to the Jetpack libraries .

关于android - 为什么 LiveData 值需要包装器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70790138/

相关文章:

android - TouchDB - CouchbaseLite 库?

android - 如何重构具有 3 个相似方法的 onButtonClick?

android - 如何检查本地 val 是否已初始化?

c# - 选中复选框时更改 wpf 数据网格行背景颜色

c# - Xamarin MvvM 内容 View 绑定(bind)

c# - 页面生命周期

java - 通过 Intent 放置 Parcelable

java - android.database.CursorIndexOutOfBoundsException...再一次

android - 如何将现有的 Angular1 Web 应用程序转换为 Cordova 应用程序?

Android Studio 任务compilefreeDebugKotlin 执行失败