如何在 Android 中使用 SingleLiveEvent 仅观察一次事件?
您是否曾经处理过 Dialog 或 SnackBar,它们在被触发/显示或关闭后,在设备旋转后再次被触发/显示?这很可能与使用 LiveData observables 在 ViewModel 和 Activity/Fragment 之间进行通信有关。
模式的相反
将 LiveData 对象用于事件可能被视为一种糟糕的设计选择,尤其是当事件只需要一次时。我们可以使用一些布尔标志来帮助视图确定是否应该触发/显示 Dialog/SnackBar。但是,这可能会导致解决方案难以理解和维护,并且没有吸引力。可以看到它是live data的类型,数据是连续消费的,但是我们只想获取一次。
没有内存泄漏:
观察者与 Lifecycle 对象相关联,并在它们所连接的生命周期被破坏时自行清理。
没有因活动停止而导致的事故:
如果观察者的生命周期是空闲的,就像在返回堆栈活动的情况下一样,它不会收到任何 LiveData 事件。 LiveData 只是遵循观察者的设计,我不相信上述用例保证它自己的可观察类变体。那么,如何解决对话问题呢?我们不应该改变 LiveData 的行为,而是应该遵循 MVVM 范式的基础知识,并在必要时让 ViewModel 告诉 ViewModel 有关被解除对话的信息。之后,VM 可能会重置或更改事件的状态。我意识到忘记提醒 ViewModel 很容易,我们正在增加 View 的职责。但是,归根结底,这是视图的工作:将任何用户操作通知 VM。
如何实现这一目标?
创建一个通用的“事件”类(可能称之为 LiveEvent)。给它observe(owner,observer) 和observeForever 函数(observer)。赋予它与 LiveData 相同的契约和功能。除此之外,LiveEvent 只是在发送数据时通知活动观察者,而不是持久值。
Kotlin
class LiveEvent {
...
fun sendingValue(data: C) {
observers.forEach { gfg ->
if (gfg.isActive()) gfg.onChanged(data)
}
}
}
Kotlin
abstract class GeeksforGeeksViewModel : ViewModel() {
protected val fireAlertedEvent = LiveEvent()
}
abstract class BaseFragment : Fragment() {
fun onViewModelInitialized() {
viewModel.fireAlertedEvent.observe(this, { fireAlert ->
fireAlert.show()
}
}
}
class GeeksforGeeksSample : GeeksforGeeksViewModel() {
fun onSaveSuccess() {
fireAlertedEvent.postValue(FireAlert("yay! we got your stuff!"))
}
}
如果您想将 LiveEvent 用于在多个屏幕上发布通知、SnackBar 或任何内容,请将它放在您的基本 ViewModel 类中。然后,在您的基本 Fragment 类中初始化 ViewModel 之后,注意该 LiveEvent。
科特林
abstract class GeeksforGeeksViewModel : ViewModel() {
protected val fireAlertedEvent = LiveEvent()
}
abstract class BaseFragment : Fragment() {
fun onViewModelInitialized() {
viewModel.fireAlertedEvent.observe(this, { fireAlert ->
fireAlert.show()
}
}
}
class GeeksforGeeksSample : GeeksforGeeksViewModel() {
fun onSaveSuccess() {
fireAlertedEvent.postValue(FireAlert("yay! we got your stuff!"))
}
}
结论
最佳解决方案是在销毁时自动取消订阅的事件源,只要没有观察者存在,就会将事件排入队列,然后将所有事件发送给第一个订阅的观察者。它可以以它认为合适的任何方式处理事件。当您需要管理流程时,使用具有不同成功/失败方法的回调是一个糟糕的主意。使用 ADT(密封类)。