Пусть каждый наблюдатель получает * новые * LiveData только после подписки / наблюдения - PullRequest
0 голосов
/ 27 апреля 2019

Каждый раз, когда вы вызываете .observe() в LiveData, наблюдатель получает последнее значение этих LiveData.Это может быть полезно в некоторых случаях, но не в моем.

  1. Всякий раз, когда я вызываю .observe(), я хочу, чтобы наблюдатель получал только будущие изменения LiveData, но не значение, которое он имеет при вызове .observe().

  2. У меня может быть несколько наблюдателей для экземпляра LiveData.Я хочу, чтобы все они получали обновления LiveData, когда они происходят.

  3. Я хочу, чтобы каждое обновление LiveData использовалось только один раз каждым наблюдателем. Я думаю, чтопросто перефразирую первое требование, но моя голова уже кружится, и я не уверен в этом.


При поиске этой проблемы я столкнулся с двумя общимиподходы:

  1. Оберните данные в LiveData<SingleEvent<Data>> и проверьте в этом классе SingleEvent, если они уже были использованы.

  2. Расширьте MediatorLiveData и используйте справочную карту, если Наблюдатель уже получил Событие

Примеры этих подходов можно найти здесь: https://gist.github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#gistcomment-2783677 https://gist.github.com/hadilq/f095120348a6a14251a02aca329f1845#file-liveevent-kthttps://gist.github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#file-event-kt

К сожалению, ни один из этих примеров не отвечает всем моим требованиям.В большинстве случаев проблема заключается в том, что любой новый Observer по-прежнему получает последнее значение LiveData после подписки.Это означает, что Snackbar, который уже был показан, отображается снова и снова, когда пользователь перемещается между экранами.


Чтобы дать вам некоторое представление о том, о чем я говорю / о чем я кодирую:

Я слежу за дизайном LiveData MVVM компонентов архитектуры Android:

  • 2 ListFragment показывает список записей.
  • Они используют 2 экземпляра одного и того же класса ViewModel для наблюдения связанных с пользовательским интерфейсом LiveData.
  • Пользователь может удалить запись в таком ListFragment.Удаление выполняется с помощью ViewModel, вызывая Repository.delete()
  • ViewModel наблюдает за репозиторием для RepositoryEvents.

Поэтому, когда удаление выполнено, репозиторий информирует об этом ViewModelи ViewModel информирует об этом ListFragment.

Теперь, когда пользователь переключается на второй ListFragment, происходит следующее:

  • Второй фрагмент создается и вызывает .observe() на своемViewModel
  • ViewModel создается и вызывает .observe() в репозитории

  • Репозиторий отправляет свой текущий RepositoryEvent в ViewModel

  • ViewModel отправляет соответствующее событие пользовательского интерфейса фрагменту
  • Фрагмент показывает снэк-бар подтверждения удаления, произошедшего где-то еще.

Вот некоторый упрощенный код:

Фрагмент:

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

Модель представления:

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

1 Ответ

0 голосов
/ 09 мая 2019

Для меня проблема была решена с помощью этого:

Класс обертки событий для хранения данных, связанных с событиями (Скопировать из примеров Google)

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 и т. д.):

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:

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:

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);
...