【问题标题】:Why each activity is getting update to the original live data instead of recent live data?为什么每个活动都更新到原始实时数据而不是最近的实时数据?
【发布时间】:2021-05-04 14:51:32
【问题描述】:

MeetingViewModel

fun observeAttendeesJoined(): LiveData<Array<AttendeeInfo>>? {
        return Repository.getAttendeesJoined()
    }

有一个使用 kotlin 的对象声明的单例存储库。 Repository 有一个 BaseActivity 正在观察的实时数据。

存储库

 fun getAttendeesJoined(): LiveData<Array<AttendeeInfo>>? {
        if (attendeesJoined == null) {
            attendeesJoined = MutableLiveData()
        }
        return attendeesJoined
    }

基础活动

private fun observeAttendeesJoined() {
        meetingViewModel.observeAttendeesJoined()?.observe(this) {
            Timber.d(" :$LOG_APP_NAME: BaseActivity: :setObservers: onAttendeesJoined: $it")
            lifecycleScope.launchWhenResumed {
                onAttendeesJoined(it)
            }
        }
    }

前台服务会更改相应可变实时数据的值。 BaseActivity 收到更新,我们正在显示小吃栏。 现在,当我们更改活动时,即使它不是由前台服务触发,也会再次触发相同的结果。

例如,如果我们在 activity A (that extends the BaseActivity) 中,并且前台服务将新与会者总数的值更改为 5,我们将在 activity A 中显示它,因为 5 用户已加入会议。用户在activity A 上花费了一些时间。一段时间后,当用户移动到activity B (that is also extending BaseActivity) 时,前台服务没有任何响应,activity B 收到与活动 A 收到的相同的最新更新,因此活动 B 也显示 5 个用户已加入的小吃吧会议,并且所有活动都继续这种模式。

MeetingViewModel

fun onAttendeesJoined(attendeeInfo: Array<AttendeeInfo>) {
        Timber.d(" :$LOG_APP_NAME: MeetingViewModel: :onAttendeesJoined: :size: ${attendeeInfo.size}")
        attendeeInfo.forEach {
            Timber.d(" :$LOG_APP_NAME: MeetingViewModel: :onAttendeesJoined: $attendeeInfo")
        }
        Repository.setAttendeesJoined(attendeeInfo)
    }

服务

 override fun onAttendeesJoined(attendeeInfo: Array<AttendeeInfo>) {
        attendeeInfo.forEach {
            Timber.d(" :$LOG_APP_NAME: ChimeService: :onAttendeesJoined: :id: ${it.attendeeId} :externalId: ${it.externalUserId} :attendeeInfo: $it :responseSize: ${attendeeInfo.size}")
        }
        meetingViewModel.onAttendeesJoined(attendeeInfo)
    }

每当前台服务更改相应的可变实时数据时,新活动(在我们的示例中为活动 B)只有在新数据 (5) 发生更改时才应获得更新,因为meetingViewModel.observeAttendeesJoined() 仅返回新数据。

如何接收活动中的唯一更新?

MeetingViewModel 的实例因每个活动而异,但存储库数据是单例(kotlin 的对象声明),不是吗?

我试图理解Transformations.mapswitchMap,但不知道如何使用它来解决问题。

谢谢你的期待。

【问题讨论】:

  • 我想建议您简化存储库。 MutableLiveData 是一个非常轻量级的对象,因此没有理由延迟加载它。即使你这样做了,你的函数也没有理由返回一个可为空的,因为当你返回它时它不可能为空。我将完全删除该功能,只公开一个公共属性val attendeesJoined = MutableLiveData()

标签: android kotlin android-livedata android-mvvm android-livedata-transformations


【解决方案1】:

LiveData 就是这样设计的。它是为了获取最新的数据状态,而不是作为消息广播者。有各种黑客可以尝试使 LiveData 用于此目的。您可以对 SingleLiveEvent 进行网络搜索,以查找有关该主题的各种文章。

您可以考虑使用 Kotlin 的 MutableSharedFlow 来代替 LiveData。它有一个 replay 参数,您可以将其设置为 0(这是默认值),这样您的新活动将不会收到已经发生的更新。在我看来,这是一个比 SingleLiveEvent 更干净的解决方案。

您的 repo 可以公开该属性:

val attendeesJoined = MutableSharedFlow<Array<AttendeeInfo>>()

你的 ViewModel 可以通过它:

val attendeesJoined: SharedFlow<Array<AttendeeInfo>> = Repository.getAttendeesJoined()

您可以使用 tryEmit() 将值发布到 MutableSharedFlow。

您的 Activity 可以在其lifecycleScopes 上收集(观察)流,这将导致与观察 LiveData 类似的行为,因为lifeCycleScope 将在 Activity 关闭时自动取消收集。

private fun observeAttendeesJoined() {
    lifecycleScope.launchWhenResumed {
        meetingViewModel.attendeesJoined.collect {
            Timber.d(" :$LOG_APP_NAME: BaseActivity: :setObservers: onAttendeesJoined: $it")
            onAttendeesJoined(it)
        }
    }
}

另外,一个建议。应该很少使用数组。存储库公开只读列表更清洁/更安全。数组是可变的和固定大小的,因此它们的用途很有限,通常用于低级工作。

【讨论】:

  • 我非常感谢您的额外建议。是的,在我将 extraBufferCapacityonBufferOverflow 添加为 val attendeesJoined = MutableSharedFlow&lt;List&lt;AttendeeInfo&gt;&gt;( extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST ) 之后,它起作用了。
【解决方案2】:

MutableLiveData 存储最后一个值并在您在另一个 Activity 中观察它时发出它(因为您将 MutableLiveData 存储在单例中)。 也许你想要SingleLiveEvent 之类的东西。见https://medium.com/@abhiappmobiledeveloper/android-singleliveevent-of-livedata-for-ui-event-35d0c58512da

【讨论】:

    猜你喜欢
    • 2020-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多