2017-02-20 3 views
2

Ich benutze die neue Bibliothek Mosby MVI für eine neue Demo-App. Beim Definieren von Absichten in einem Presenter ist es inkonsistent, wenn die Absicht beim Anhängen der Ansicht ausgelöst wird.Mosby MVI: Inkonsequentes Bindungsverhalten der Absicht

Zum Beispiel: Lassen Sie uns definieren sehr einfache Absicht in einer Aktivität

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

Der Präsentator bindet die Absicht wie:

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

Diese Absicht ganz gut funktioniert. Wenn Sie zu einer anderen Aktivität (Detailansicht) navigieren und zurück navigieren, wird bindIntents() als die Absicht bezeichnet, die neu erstellt wird. intentLoadData() gibt kein neues Element aus, und der MviBasePresenter wird den vorherigen ViewState mit dem internen BehaviorSubject bereitstellen.

Mein Problem ist: Wenn ich den Intent (zum Nachladen der Daten) leicht anpassen. Die Absicht beginnt, ein Element auszugeben, wenn die Ansicht erneut angefügt wird.

lässt also die Absicht ändern:

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

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

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

Nein, wenn auf eine neue Aktivität der Navigation und zurück. Die Absicht gibt ein neues Objekt aus, wenn die Ansicht erneut angefügt wird. In meinem Fall führt dies zu einem neuen APU-Aufruf an das Backend, um die Daten neu zu laden, anstatt den letzten ViewState erneut zu verwenden. Dies passiert auch, wenn reloadData() nie aufgerufen wird.

Dieses Verhalten fühlt sich sehr inkonsistent an. Wie kann ich mehr Kontrolle haben, wenn beim Wiederanbringen der Ansicht eine Absicht ausgelöst wird?

Update: Für mich noch interessanter ist, wie kann ich die automatische Emissions der Absichten zu vermeiden, wenn wieder anbringen, ohne die beobachtbaren Abschluss. Mit der Einführung eines PublishSubject lädt die Aktivität die gesamten Daten neu, selbst wenn sie sich gerade drehen.

Antwort

2

meine eigene Frage zu beantworten, und die Kommentare zu wickeln, das ist meine Lösung:

Zunächst müssen wir verstehen, wie Mosby3 MVI eine Ansicht wieder her, zum Beispiel: nach der Rotation, die Navigation vor und zurück zu unterschiedlichen Ansichten . Mosby3 behält die Instanz des Präsentators bei. Wenn eine neue Instanz der Ansicht erstellt wird, wird der Präsentator wiederhergestellt und an die Ansicht angehängt. onStart() der neuen Ansicht aktualisiert der Presenter die Absichten. Daher erstellt die neue Ansicht neue Absichten, und der Präsentator wird sie unter Verwendung von PublishSubject s abonnieren.

Wenn die Absicht der vorherigen Ansicht onComplete() ausgegeben wurde, ist die PublishSubject ebenfalls abgeschlossen und der Stream wird geschlossen. Die (Interaktor-) Logik, die an diese Absicht gebunden ist, wird abgemeldet. Daher kann diese Absicht nicht mehr durch die Ansicht ausgelöst werden.

In dem Beispiel der ursprünglichen Frage. Observable.just(true) schließt den Stream. Auch wenn die Ansicht und ihre Absichten neu erstellt werden (nach der Rotation), wird kein neuer Gegenstand ausgegeben. mReloadDataSubject.startWith(true) stattdessen emittiert onComplete() und der Stream isn t closed. When the presenter resubscribes to that intent (after rotation), the intent emits the startsWith (true) `. Im Beispiel führt dies zu einem vollständigen Neuladen der Daten bei jeder Rotation.

Um die Intents bei einem bedingten Nachladen auszulösen RxNavi kann sehr hilfreich sein.

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

Mosby MVI respektiert den Reactive Stream-Vertrag. Werfen Sie einen Blick auf intentLoadData()

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

Observable.just(true) nicht nur Anrufe onNext(true) sondern auch Anrufe onCompleted(). Sobald ein reaktiver Stream abgeschlossen ist, kann kein weiterer Gegenstand mehr durch den Stream gesendet werden. Nach onComplete() ist der beobachtbare Strom dauerhaft geschlossen.

ein PublishSubject Verwendung ist völlig in Ordnung, in diesem Fall, aber zur besseren Lesbarkeit würde ich vorschlagen, nicht startWith() zu verwenden, sondern etwas zu tun:

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. Sie könnten auch Bibliotheken wie Navi von Trello verwenden, die Observable-Stream für Lifecycle-Ereignisse bieten, aber bedenken Sie, dass Navi ein onCompleted()-Ereignis ausgibt, wenn die Aktivität zerstört wird (dh während der Bildschirmausrichtung), so dass Sie in der gleichen Situation landen: Sie haben um sicherzustellen, dass onCompleted() nicht aufgerufen wird, wenn Sie die Absicht später erneut auslösen möchten.

+0

Mein Problem ist mehr umgekehrt. Die Verwendung eines PublishSubject hilft später Intents auszulösen, aber wie kann ich verhindern, dass es in bindIntents() 'emittiert, wenn ich die Ansicht wieder anschließe? – TobiasRe

+0

Was meinst du mit "Wiederanbringen der Ansicht"? Können Sie mir ein konkretes Beispiel geben? – sockeqwe

+0

Ich verwende eine PublishSubject-Definition zum Laden von Daten von einem Web-Service wie in meiner ursprünglichen Frage. Die Aktivität startet und die Daten werden geladen. Wenn ich das Gerät rotiere, wird die Ladeabsicht während 'bindIntentActually()' im MviBasePresenter erneut ausgelöst. Ich möchte dieses unnötige Nachladen vermeiden. 'bindIntentActually()' wird in 'attachView()' mit 'viewAttachedFirstTime'set in diesem Fall auf false gesetzt. Deshalb beziehe ich mich darauf, wieder anzubringen. – TobiasRe

Verwandte Themen