android 如何创建LiveData,它发出单个事件并只通知最后订阅的观察者?

aiqt4smr  于 2023-01-28  发布在  Android
关注(0)|答案(5)|浏览(190)

我创建了实时数据,它发出单个事件,如example中所示。
我的问题是:如何在LiveData中的值改变时只通知最后订阅的观察者?
我想到的是将观察者存储在SingleLiveData类的链表中,然后仅当传递的观察者与列表的最后一个元素相同时才调用super.observe
我不确定这是不是最好的办法。
我想使用这个机制将FAB点击事件从活动传播到显示在ViewPager内部的片段。片段是动态添加到视图分页适配器的,所以假设我们知道片段的顺序。

jum4pzuy

jum4pzuy1#

最后,我找到了一个解决这个问题的方法:我不得不放弃那些只发出一个事件的实时数据,因为它不能按照我需要的方式工作。
相反,我使用了简单的可变实时数据,它发出一个事件对象,该对象 Package 了一个数据,如Jose Alcérreca编写的article的最后一段所示。
我在视图页中显示片段,因此此时只有一个可见片段。
我的视图模型如下所示:

class ActionViewModel : ViewModel() {
  private val onCreateLiveData: MutableLiveData<Event<String>> = MutableLiveData()

  fun observeOnCreateEvent(): LiveData<Event<String>> = onCreateLiveData

  fun onCreateCollectionClick(message: String) {
    this.onCreateLiveData.value = Event(message)
  }
}

事件 Package 类实现如下所示:

/*Used as a wrapper for data that is exposed via a LiveData that represents an 
 event.*/

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

现在我们可以观察到这样的片段:

override fun onActivityCreated(savedInstanceState: Bundle?) {
   super.onActivityCreated(savedInstanceState)

   actionViewModel = ViewModelProviders.of(requireActivity()).get(ActionViewModel::class.java)
   actionViewModel.observeOnCreateEvent()
       .observe(this, Observer {
         it?.takeIf { userVisibleHint }?.getContentIfNotHandled()?.let {
           //DO what ever is needed
         }
       })
}

如果片段当前对用户可见,则FragmentuserVisibleHint属性将返回True。由于我们此时仅显示一个片段,因此这对我们有效。这意味着片段将仅访问事件数据(如果可见)。
另外,Event Package 器的实现只允许读取一次值,因此,下次Observer获取此事件时,其值将为null,我们将忽略它。

结论:这样,我们模拟的是仅通知最后订阅的观察者的单个事件实时数据。

qeeaahzv

qeeaahzv2#

如果你使用Kotlin,你可以用Flow替换LiveDataStateFlow可以用来替换常规的LiveData,而SharedFlow可以用来处理无状态事件。它还将为你提供null safety和Flow自带的所有运算符和配置。
迁移在here等地方进行了描述,下面是一个基本示例:
视图模型:

interface MyViewModel {
    val myData: StateFlow<MyData>
    val myEvents: SharedFlow<MyEvent>
}

class MyViewModelImpl: MyViewModel {
    override val myData = MutableStateFlow(MyData())
    override val myEvents = MutableSharedFlow<MyEvent>(replay = 0, extraBufferCapacity = 1, BufferOverflow.DROP_OLDEST)

    /*
     * Do stuff 
     */
}

活动:

lifecycleScope.launch {
    myData.collect {
        // handle stateful data    
    }
}

lifecycleScope.launch {
    myEvents.collect {
        // handle stateless events    
    }
}

请注意,lifecycleScope需要相应的ktx dependency
Herer's更多阅读关于Flow在Android.

5t7ly7z5

5t7ly7z53#

我精心设计了一个解决方案,请随意查看https://github.com/ueen/LiveEvent

ivqmmu1c

ivqmmu1c4#

我创建了一个库来处理在处理事件驱动的数据场景时可能遇到的最常见情况
https://github.com/javaherisaber/LiveX
它包含以下类型的类:
| 实时数据|实时事件|OneShot实时事件|单个实时事件|
| - ------|- ------|- ------|- ------|
| 可以注册多个观察者,所有观察者都会根据生命周期接收事件|可以注册多个观察者,每个观察者只能接收事件一次|只有一个观察者只能注册和接收事件一次|可以注册多个观察者,只有第一个观察者接收事件|

xjreopfe

xjreopfe5#

我在LD扩展中找到了解决方案:

fun <T> LiveData<T>.observeAsEvent(owner: LifecycleOwner, observer: Observer<in T>) {
    var previousKey: Any? = value?: NULL
    observe(owner) { value ->
        if (previousKey == NULL || previousKey != value) {
            previousKey = value
            observer.onChanged(value)
        }
    }
}

private const val NULL = "NULL"

此的用法:

viewModel.resultLiveData.observeAsEvent(viewLifecycleOwner) { 
     ...
}

相关问题