Mosby MVI: непоследовательное поведение, связывающее намерения

Я использую новую библиотеку Mosby MVI для нового демонстрационного приложения. При определении намерений в презентаторе это противоречиво, когда намерение инициируется / испускается, когда представление присоединено.

Например: давайте определим очень простое намерение в деятельности

public Observable<Boolean> intentLoadData(){
  return Observable.just(true);
}

Ведущий связывает намерение как:

@Override
protected void bindIntents() {
  Observable<MailListViewState> loadData = intent(ExampleViewContract::intentLoadData).flatMap(interactor::loadData)
            .observeOn(AndroidSchedulers.mainThread());
  subscribeViewState(loadData, ExampleViewContract::render);
}

Это намерение работает просто отлично. При переходе к другому виду деятельности (подробный вид) и переходе назад, bindIntents()называется намерение воссоздано. intentLoadData()не генерирует новый элемент, и MviBasePresenter предоставит предыдущий ViewState, используя внутренний BehaviorSubject.

Моя проблема: когда я немного настраиваю Intent (для перезагрузки данных). Намерение начинает испускать элемент при повторном присоединении к представлению.

Итак, давайте изменим намерение на:

private PublishSubject<Boolean> mReloadDataSubject = PublishSubject.create();

private void reloadData(){
  mReloadDataSubject.onNext(true);
}

public Observable<Boolean> intentLoadData(){
  return mReloadDataSubject.startWith(true);
}

Нет при переходе к новому занятию и обратно. Намерение испускает новый элемент при повторном присоединении представления. В моем случае это приводит к новому обращению APU к бэкэнду для перезагрузки данных, а не к повторному использованию последнего ViewState. Это происходит даже когда reloadData() никогда не называется.

Такое поведение кажется очень противоречивым. Как я могу чувствовать больше контроля, когда намерение срабатывает во время повторного присоединения вида?

Обновление: для меня еще более интересным является то, как я могу избежать автоматической генерации намерений при повторном присоединении, не завершая Observable. С введением PublishSubject действие перезагрузит все данные, даже когда они просто вращаются.

2 ответа

Решение

Чтобы ответить на мой собственный вопрос и обернуть комментарии, это мое решение:

Сначала мы должны понять, как Mosby3 MVI восстанавливает вид, например: после поворота, навигации вперед и назад к различным видам. Mosby3 сохраняет экземпляр ведущего. Когда будет создан новый экземпляр представления, докладчик будет восстановлен и присоединен к представлению. onStart()нового представления, докладчик обновит намерения. Следовательно, новый вид создает новые намерения, и докладчик будет подписываться на них, используя PublishSubjects.

Если намерение предыдущего представления испущено onComplete()PublishSubjectтакже завершается и поток закрывается. Логика (интерактора), связанная с этим намерением, будет отписана. Следовательно, это намерение больше не может быть вызвано просмотром.

На примере оригинального вопроса. Observable.just(true) закрывает поток Даже когда представление и его намерения воссоздаются (после поворота), новый элемент не генерируется. mReloadDataSubject.startWith(true) вместо этого не излучает onComplete() и поток неt closed. When the presenter resubscribes to that intent (after rotation), the intent emits theStartsWith (истина)`. В этом примере это вызывает полную перезагрузку данных при каждом обороте.

Для того, чтобы вызвать намерения при условной перезагрузке, RxNavi может быть очень полезным.

public Observable<Boolean> intentReloadData() {
     //check if the data needs a reload in onResume()
     return RxNavi.observe(this, Event.RESUME)
                  .filter(ignored -> mNeedsReload == true)
                  .map(ignored -> true);
}

Mosby MVI соблюдает контракт с реактивными потоками. Взгляни на intentLoadData()

public Observable<Boolean> intentLoadData(){
  return Observable.just(true);
}

Observable.just(true) не только звонки onNext(true) но и звонки onCompleted(), Как только Реактивный поток завершен, никакие другие элементы не могут быть переданы через поток. После onComplete() наблюдаемый поток закрыт навсегда.

Используя PublishSubject в этом случае прекрасно, но для лучшей читабельности я бы предложил не использовать startWith() а лучше сделать что-то вроде этого:

public class MyActivity extends MviActivity<MyView, MailListViewState> {
  private PublishSubject<Boolean> mReloadDataSubject = PublishSubject.create();

  public void onResume(){
    super.onResume();
   // Triggers on screen orientation changes and
   // when navigating back to this screen from back stack
    mReloadDataSubject.onNext(true);
  }

  public Observable<Boolean> intentLoadData(){
    return mReloadDataSubject;
  }

}

Btw. Вы также можете использовать библиотеки, такие как Navi из Trello, которые предлагают поток Observable для событий жизненного цикла, но имейте в виду, что Navi испускает onCompleted() событие, если действие уничтожено (т. е. во время изменения ориентации экрана), поэтому вы попадаете в ту же ситуацию: вы должны убедиться, что onCompleted() не вызывается, если вы хотите запустить намерение снова позже.

Другие вопросы по тегам