订阅/观察后​​,让每个观察者仅接收* new * LiveData [英] Let every Observer only receive *new* LiveData upon subscribing/observing

本文介绍了订阅/观察后​​,让每个观察者仅接收* new * LiveData的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每当您在LiveData上调用.observe()时,观察者都会收到该LiveData的最后一个值.在某些情况下这可能很有用,但在我的情况下却没有.

Whenever you call .observe() on LiveData, the Observer receives the last value of that LiveData. This may be useful in some cases, but not in mine.

  1. 每当我调用.observe()时,我都希望观察者仅接收将来的LiveData更改,而不希望接收.observe()时它持有的值.

  1. Whenever I call .observe(), I want the Observer to receive only future LiveData changes, but not the value it holds when .observe() is called.

我可能有多个LiveData实例的观察者.我希望他们在发生时都能收到LiveData更新.

我希望每个LiveData更新仅由每个观察者使用一次.我认为这只是对第一个要求的重新表述,但我的头已经在旋转,我正在不确定.

I want each LiveData update to be consumed only once by each Observer. I think is just a re-phrasing of the first requirement, but my head is spinning already and I'm not sure about it.


在搜索这个问题时,我遇到了两种常见的方法:


While googling this problem, I came upon two common approaches:

  1. 将数据包装在LiveData<SingleEvent<Data>>中,并检查此SingleEvent类是否已被使用.

  1. Wrap the data in an LiveData<SingleEvent<Data>> and check in this SingleEvent class if it was already consumed.

扩展MediatorLiveData并在观察者已经收到事件的情况下使用查找映射

Extend MediatorLiveData and use a look-up-map if the Observer already got the Event

这些方法的示例可以在这里找到: https://gist.github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#gistament-2783 > https://gist.github.com/hadilq/f095120348a6a14251a02aca329f1845#file-liveevent- kt https://gist.github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#file-event- kt

Examples for these approaches can be found here: https://gist.github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#gistcomment-2783677 https://gist.github.com/hadilq/f095120348a6a14251a02aca329f1845#file-liveevent-kt https://gist.github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#file-event-kt

不幸的是,这些示例都不能解决所有的要求.大多数情况下,问题在于任何新的观察者在订阅后仍会收到最后一个LiveData值.这意味着,每当用户在屏幕之间导航时,就会一次又一次显示已经显示的Snackbar.

Unfortunately none of these examples solves all my requirements. Most of the time, the problem is that any new Observer still receives the last LiveData value upon subscribing. That means that a Snackbar which was already shown is displayed again and again whenever the user navigates between screens.

为了给您一些见解,我在说什么/我在写什么:

To give you some insights what I am talking about / what I am coding about:

我正在关注Android Architecture Componentns的LiveData MVVM设计:

I am following the LiveData MVVM design of the Android Architecture Componentns:

  • 2 ListFragment正在显示条目列表.
  • 他们正在使用同一ViewModel类的2个实例来观察与UI相关的LiveData.
  • 用户可以删除这样的ListFragment中的条目.删除是通过ViewModel调用Repository.delete()
  • 完成的
  • ViewModel观察RepositoryEvents的存储库.
  • 2 ListFragment are showing a list of entries.
  • They are using 2 instances of the same ViewModel class to observe UI-related LiveData.
  • The user can delete an entry in such a ListFragment. The deletion is done by the ViewModel calling Repository.delete()
  • The ViewModel observes the Repository for RepositoryEvents.

因此,删除完成后,存储库会将其通知给ViewModel,而ViewModel会将其通知给ListFragment.

So when the deletion is done, the Repository informs the ViewModel about it and the ViewModel inform the ListFragment about it.

现在,当用户切换到第二个ListFragment时,将发生以下情况:

Now, when the user switches to the second ListFragment the following happens:

  • 创建第二个片段,并在其ViewModel上调用.observe()
  • 将创建ViewModel并在存储库上调用.observe()

存储库将其当前的RepositoryEvent发送到ViewModel

The Repository sends its current RepositoryEvent to the ViewModel

这里有一些简化的代码:

Heres some simplified code:

片段:

viewModel.dataEvents.observe(viewLifecycleOwner, Observer { showSnackbar() })
viewModel.deleteEntry()

ViewModel:

val dataEvents: LiveData<EntryListEvent> = Transformations.switchMap(repository.events, ::handleRepoEvent)
fun deleteEntry() = repository.deleteEntry()
private fun handleRepoEvent(event: RepositoryEvent): LiveData<EntryListEvent> {
    // convert the repository event to an UI event
}

存储库:

private val _events = MutableLiveData<RepositoryEvent>()
val events: LiveData<RepositoryEvent>
    get() = _events

fun deleteEntry() {
    // delete it from database
    _events.postValue(RepositoryEvent.OnDeleteSuccess)
}

推荐答案

对我来说,此问题已解决:

For me problem was solved with this:

事件包装器类,用于保留与事件相关的数据(从Google示例中复制)

Event wrapper class to keep event related data(Copy from google samples)

public class Event<T> {

    private T mContent;

    private boolean hasBeenHandled = false;


    public Event( T content) {
        if (content == null) {
            throw new IllegalArgumentException("null values in Event are not allowed.");
        }
        mContent = content;
    }

    @Nullable
    public T getContentIfNotHandled() {
        if (hasBeenHandled) {
            return null;
        } else {
            hasBeenHandled = true;
            return mContent;
        }
    }

    public boolean hasBeenHandled() {
        return hasBeenHandled;
    }
}

接下来,我创建事件观察器类,该类处理数据检查(null等):

Next, i create event observer class, that handles data checks(null, etc):

public class EventObserver<T> implements Observer<Event<T>> {

  @Override
  public void onChanged(Event<T> tEvent) {
    if (tEvent != null && !tEvent.hasBeenHandled())
      onEvent(tEvent.getContentIfNotHandled());
  }

  protected void onEvent(@NonNull T content) {}
}

此外,事件处理程序类用于简化从viewmodel的访问:

And, event handler class, to simplify access from viewmodel:

public class EventHandler<T> {

  private MutableLiveData<Event<T>> liveEvent = new MutableLiveData<>();

  public void observe(@NonNull LifecycleOwner owner, @NonNull EventObserver<T> observer){
      liveEvent.observe(owner, observer);
  }

    public void create(T content) {
    liveEvent.setValue(new Event<>(content));
  }
}

示例:

在ViewModel.class中:

In ViewModel.class:

private EventHandler<Boolean> swipeEventHandler = new EventHandler<>();

  public EventHandler<Boolean> getSwipeEventHandler() {
    return swipeEventHandler;
  }

活动/片段中:

开始观察:

 viewModel
    .getSwipeEventHandler()
    .observe(
        getViewLifecycleOwner(),
        new EventObserver<Boolean>() {
          @Override
          protected void onEvent(@NonNull Boolean content) {
            if(content)confirmDelete(modifier);
          }
        });

创建事件:

viewModel.getSwipeEventHandler().create(true);

这篇关于订阅/观察后​​,让每个观察者仅接收* new * LiveData的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆