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()
对于新视图,演示者将更新意图。因此,新视图创建新意图,演示者将使用 PublishSubject
s.
订阅它们
如果发出前一个视图的意图 onComplete()
,那么 PublishSubject
也会完成并且流会关闭。绑定到此意图的(交互器)逻辑将被取消订阅。因此,此意图不能再由视图触发。
在原题的例子中。 Observable.just(true)
关闭流。即使重新创建视图及其意图(旋转后),也不会发出新项目。
mReloadDataSubject.startWith(true)
而不是发出 onComplete()
并且流是 t closed. When the presenter resubscribes to that intent (after rotation), the intent emits the
startsWith(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);
}
我正在为新的演示应用程序使用新的 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()
对于新视图,演示者将更新意图。因此,新视图创建新意图,演示者将使用 PublishSubject
s.
如果发出前一个视图的意图 onComplete()
,那么 PublishSubject
也会完成并且流会关闭。绑定到此意图的(交互器)逻辑将被取消订阅。因此,此意图不能再由视图触发。
在原题的例子中。 Observable.just(true)
关闭流。即使重新创建视图及其意图(旋转后),也不会发出新项目。
mReloadDataSubject.startWith(true)
而不是发出 onComplete()
并且流是 t closed. When the presenter resubscribes to that intent (after rotation), the intent emits the
startsWith(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);
}