Mosby MVI:不一致的意图绑定行为

Mosby MVI: Inconsistent intent binding behavior

我正在为新的演示应用程序使用新的 Mosby MVI 库。 在演示者中定义意图时,当附加视图时意图为 triggered/emitted 时不一致。

例如:让我们在 activity

中定义非常简单的意图
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);
}

这个意图工作得很好。当导航到另一个 activity(详细视图)并导航回来时,bindIntents() 被称为重新创建意图。 intentLoadData()不会发出新项目,MviBasePresenter 将使用内部 BehaviorSubject 提供先前的 ViewState。

我的问题是: 当我稍微调整 Intent(用于重新加载数据)时。当重新附加视图时,意图开始发出一个项目。

所以让我们将意图更改为:

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

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

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

导航到新 activity 并返回时否。重新附加视图时,意图会发出一个新项目。在我的例子中,这会导致对后端进行新的 APU 调用以重新加载数据,而不是重新使用最后一个 ViewState。即使从未调用 reloadData() 也会发生这种情况。

这种行为感觉很不协调。在重新附加视图期间触发 intent 时,如何让我感觉更有控制力?

更新: 对我来说更有趣的是,我如何避免在重新附加时自动发出意图,而不完成 Observable。 随着 PublishSubject 的引入,activity 即使只是旋转也会重新加载整个数据。

Mosby MVI 遵守 Reactive Streams 合同。看看intentLoadData()

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

Observable.just(true)不仅调用了onNext(true),还调用了onCompleted()。一旦 Reactive 流完成,就不能再通过该流发出任何项目。在 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;
  }

}

顺便说一句。您还可以使用 Trello 中的 Navi 等库,它为生命周期事件提供 Observable 流,但请记住,如果 activity 被破坏(即在屏幕方向更改期间),Navi 会发出 onCompleted() 事件) 所以你最终会遇到同样的情况:如果你想稍后再次触发意图,你必须确保 onCompleted() 不会被调用。

为了回答我自己的问题并总结评论,这是我的解决方案:

首先我们要了解Mosby3 MVI是如何恢复视图的,例如:旋转后,前后导航到不同的视图。 Mosby3 保留了演示者的实例。当视图的一个新实例被创建时,presenter 将被恢复并附加到视图。 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(true)`。在示例中,这会导致每次旋转时都完全重新加载数据。

为了触发有条件重新加载的意图 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);
}