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

时间:2018-08-08 23:59:56

标签: android android-architecture-components android-jetpack android-livedata

我创建了实时数据,它会像此example一样发出单个事件。

下一个是我的问题: 当LiveData中的值更改时,如何仅通知最后订阅的观察者?

我想到的是将观察者存储在SingleLiveData类的链接列表中,然后仅在传递的观察者与列表的最后一个元素相同时才调用super.observe

我不确定这是否是最好的方法。

我想使用这种机制将FAB点击事件从活动传播到ViewPager内部显示的片段。片段是动态添加到视图寻呼机适配器的,因此,我们知道了片段的顺序。

3 个答案:

答案 0 :(得分:1)

最后,我找到了解决此问题的方法。我不得不离开发出单个事件的实时数据,因为它无法按照我需要的方式运行。

相反,我使用了简单的可变实时数据,该数据会发出事件对象,该事件对象像JoseAlcé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)
  }
}

事件包装器类的实现如下:

/*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
         }
       })
}
如果片段当前对用户可见,则

片段 userVisibleHint 属性将返回true。由于我们当时只显示一个片段,因此对我们有用。这意味着该片段将仅访问可见的事件数据。

此外,事件包装器的实现只允许读取一次该值,因此,每当Observer下次获取此事件时,其值将为null,我们将忽略它。

结论:通过这种方式,我们可以模拟单个事件实时数据,该数据仅通知最后订阅的观察者。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

如果您使用的是 Kotlin,则可以将 LiveData 替换为 FlowStateFlow 可用于替换常规 LiveData,而 SharedFlow 可用于无状态事件。它还将为您提供 null 安全性以及 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 有关 Android 中 Flow 的更多阅读。