2016-12-10 2 views
1

Ich benutze RxJava und Retrofit, um eine App zu erstellen, und ich habe dieses Szenario, dass in meiner Ansicht eine Liste von Elementen vom Server geladen und dem Benutzer angezeigt wird. Der Benutzer kann die Daten aktualisieren, indem er auf eine Aktualisierungsschaltfläche klickt. Das Folgende ist eine grobe Implementierung meines Szenarios mit MVP.Wie manuell Ereignis von Observable anfordern?

// This is my retrofit adapter service 
public interface ApiService{ 
    @GET("items"); 
    Observable<JsonElement> getItemsList(); 
} 

// This is my presnter 
public class MyPresnter{ 
    ... 
    public Observable<List<Item>> loadItemsList(){ 
     return apiService.getItemsList().map(jsonElement -> { 
      // here I convert my api response to a List of Items 
      return new ArrayList<Item>(); 
     }); 
    } 
} 

// And inside my view I use RxBinding to bind my button click events 
RxView.clicks(refreshBtn).subscribe(view -> mPresenter.loadItemsList() 
              .subscribeOn(Schdulers.io()) 
              .observeOn(AndroidSchedulers.mainThread()) 
              .subscribe(list -> {/* update view */}) 
            ); 

Nun meine Frage ist, wie soll ich Daten anfordern, ohne dass die refreshBtn zum Beispiel klicken, wenn der Benutzer zuerst die Ansicht gelangt? Sollte ich refreshBtn.performOnClick() verwenden?
Oder gibt es eine Möglichkeit, Ereignis auf einem bereits abonnierten Observable manuell anzufordern?

+1

Ich verstehe, warum Sie RxJava hier verwenden möchten (ich war auch besessen davon, es überall zu benutzen, nachdem ich etwas darüber gelernt habe), aber Ihr Anwendungsfall braucht wahrscheinlich überhaupt keine reaktive Vorgehensweise. Machen Sie es einfach so, wie Sie es bei AsyncTask tun würden: Erklären Sie eine "Subskriptions" -Eigenschaft auf Ihrem Bildschirm, weisen Sie sie beim Abonnieren dem Ergebnis von "Observable # subscribe" zu und löschen Sie sie, wenn die Hintergrundoperation abgeschlossen ist. Beachten Sie, dass RxJava-Klassen viel Overhead haben, also Methoden wie 'RxView.clicks' zu verwenden, wie Sie es tun (ohne sie tatsächlich mit anderen Operatoren zu verketten), ist eine ziemlich schlechte Idee. – user1643723

Antwort

3

Um dies zu beantworten, müssen wir die Logik des Stroms in Frage zu stellen. Es ist in der Regel schlechte Praxis zu einem neuen beobachtbaren in einer subscribe-Methode eines früheren zu abonnieren. Es mag Fälle geben, in denen es vielleicht keinen besseren Weg gibt, aber es ist zumindest ein Indiz für eine Untersuchung. Zusammensetzung der Ströme ist einer der größten Vorteile, die RxJava hat.

Die Logik hier schlägt vor, dass es eine Reihe von Ereignissen (Klicks und manuelle Aufrufe) gibt, auf die Sie mit dem Abonnieren eines anderen Streams reagieren möchten. Es gibt einen Operator, der genau dieses Verhalten hat: flatMap.

Dank flatMap Operator, haben wir bereits Mittel, wenn etwas geschieht zu reagieren:

refreshEventStream 
    .flatMap(object -> { 
     return mPresenter.loadItemsList().subscribeOn(Schdulers.io()) 
    }) 
    .subscribe(list -> /* update UI */) 

Nun müssen wir etwas tatsächlich darstellt, um herauszufinden, was passiert. Wie Sie bereits beschrieben haben, gibt es derzeit zwei Möglichkeiten, die Aktualisierung aufzurufen. Durch ein Klick-Ereignis und manuelle Aktualisierung. Mit anderen Worten wollen wir diese Ereignisse in einen Stream verschmelzen. Auch hier gibt es einen netten und glänzenden Operator namens mergeWith, den wir verwenden können. Für den manuellen Aufruf müssen wir jedoch Subject (http://reactivex.io/documentation/subject.html) erstellen, das sowohl wie ein Observable als auch ein Subscriber fungiert.

PublishSubject<Void> refreshSubject = PublishSubject.create() 
refreshEventStream = refreshSubject 
          .mergeWith(Rx.clicks(refreshBtn).throttleFirst(500, TimeUnit.MILLISECONDS)) 

refreshEventStream 
    .flatMap(object -> { 
     return mPresenter.loadItemsList().subscribeOn(Schdulers.io()) 
    }) 
    .subscribe(list -> /* update UI */) 

Ich habe throttleFirst Operator auf den Click-Stream, wie Sie wahrscheinlich nicht wollen, Benutzer Spam mit Anfragen wie diese lassen :-) Das einzige, was sie tut, ist, dass es ein Ereignis aus diesem Strom und ignoriert nimmt den Rest der Emissionen für eine bestimmte Zeit, bevor sie erneut reagieren.

+0

Danke für diese erstaunliche Antwort.Deshalb falle ich für RxJava! Prost. ;) –

3

Ich denke, Sie könnten performOnClick anrufen, vorausgesetzt, Sie haben die RxView.clicks() zuvor eingerichtet.

Im Allgemeinen können Sie in einem Subject zusammenführen möchten, dass auch die Vorgänge auslösen können:

PublishSubject<Object> manualRefresh = PublishSubject.create(); 

RxView.clicks(refreshBtn) 
.cast(Object.class) 
.mergeWith(manualRefresh.onBackpressureLatest()) 
.switchMap(v -> mPresenter.loadItemsList() 
       .subscribeOn(Schdulers.io()) 
       .observeOn(AndroidSchedulers.mainThread()) 
) 
.subscribe(list -> /* update */); 

manualRefresh.onNext("Now!"); 
Verwandte Themen