2014-12-01 19 views
13

Ich suche ein Beispiel für einen Fluss, den ich mit Hilfe von RxJava implementieren möchte.RxJava Netzwerkanforderungen und Caching

Angenommen, ich möchte eine Liste von Daten anzeigen. Der Ablauf sollte in etwa so aussehen:

  1. Cache lesen. Wenn es die Daten enthält, zeigen Sie es an;
  2. eine API-Anfrage an den Server senden:

    Wenn es die Daten zurückgegeben, es dann und es zeigen zwischenzuspeichern.

    Wenn es zurückgegeben und Fehler, und es gab keine im Cache gespeicherten Daten zeigen dann einen Fehler.

    Wenn es zurückgegeben und Fehler und es war etwas im Cache gespeichert, dann nichts zu tun.

Im Moment habe ich ein method that does something similar (mit viel Inspiration von Jake's u2020). Der Hauptunterschied besteht darin, dass das In-Memory-Caching verwendet wird, so dass kein separates Observable zum Lesen aus dem Cache benötigt wird. Dies kann auch synchron erfolgen.

Ich weiß nicht, wie zwei Observablen kombinieren (eine für aus dem Cache und die andere für API-Aufruf zu lesen) und die oben beschriebene Strömung erhalten.

Irgendwelche Vorschläge?

+0

Sieht aus, Sie brauchen 'amb': https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators#amb – zsxwing

+0

@zsxwing könnten Sie ein Beispiel geben?Ich bin offen für Vorschläge, obwohl ich eine Antwort gefunden habe (die nicht perfekt ist). –

+0

Ich würde die Cache-Behandlung eher dem Netzwerk-Client überlassen. Wenn Sie HTTP verwenden, gibt es Header in der Antwort, die dediziert sind, um dem Client anzuzeigen, welche Art von Cache implementiert werden soll, wie lange das Objekt aufbewahrt werden soll. Nicht zu vergessen, dass es auch die Möglichkeit gibt, zu überprüfen, ob Ihr Cache ist noch gültig und aktualisieren Sie es bei Bedarf (Rückgabecode 304). – njzk2

Antwort

1

Hier ist meine Lösung:

readDataFromCache().defaultIfEmpty(null) 
     .flatMap(new Func1<Data, Observable<Data>>() { 

      @Override 
      public Observable<Data> call(final Data data) { 
       if (data == null) { 
        // no cache, show the data from network or throw an error 
        return apiCall(); 
       } else { 
        return Observable.concat(
          Observable.just(data), 
          // something cached, show the data from network or do nothing. 
          apiCall().onErrorResumeNext(Observable.<Data>empty())); 
       } 
      } 
     }); 

ich nicht hinzufügen, die subscribeOn und observeOn, weil ich nicht sicher bin, readDataFromCache()ioScheduler oder uiScheduler verwenden sollten.

+0

Das einzige Problem, das ich damit hatte, ist, wenn 'readDataFromCache()' einen Fehler zurückgibt. 'onErrorResumeNext (Observable. empty())' sollte vor 'defaultIfEmpty (null)' hinzugefügt werden, um den Fehler zu verschlucken. –

2

Ich glaube, ich habe mein Problem gelöst. Die beobachtbare Kette sieht so aus:

apiCall() 
     .map(data -> dataInMemory = data) 
     .onErrorResumeNext(t -> data == null ? 
       Observable.just(Data.empty()) : Observable.empty()) 
     .startWith(readDataFromCache().map(data -> dataInMemory = data)) 
     .subscribeOn(ioScheduler) 
     .observeOn(uiScheduler) 
     .subscribe(dataRequest); 

Der wichtigste Punkt ist, dass, wenn readDataFromCache() einen Fehler wirft, wird es onCompleted() aufrufen, ohne onError() aufrufen. Es sollte also eine benutzerdefinierte Observable sein, die Sie steuern können.

Data.empty() ist ein Stub für meine Daten - die Subscriber sollte es als Fehler behandeln.

dataInMemory ist Mitglied in meiner Steuerung, die im Cache-Speicher fungiert.


EDIT: die Lösung nicht richtig funktioniert. Der Abschluss eines Anwendungsfalles (siehe Kommentar) wird nicht erreicht.

EDIT 2: gut, die Lösung funktioniert funktioniert nach einigen Feinabstimmungen. Der Fix gab verschiedene Typen von Observablen zurück, abhängig vom Status des In-Memory-Caches. Irgendwie schmutzig.

+0

Nun, meine Lösung funktioniert nicht. Wenn 'readDataFromCache()' gut läuft und einige Daten anzeigt und 'apiCall()' einen Fehler zurückgibt, wird der Fehler angezeigt. –