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()
нового представления, докладчик обновит намерения. Следовательно, новый вид создает новые намерения, и докладчик будет подписываться на них, используя 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 (истина)`. В этом примере это вызывает полную перезагрузку данных при каждом обороте.
Для того, чтобы вызвать намерения при условной перезагрузке, 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()
не вызывается, если вы хотите запустить намерение снова позже.