Ich arbeite am Networking für meine App. Also habe ich beschlossen, Square's Retrofit auszuprobieren . Ich sehe, dass sie einfach unterstützenCallback
@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);
und RxJava Observable
@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);
Beide sehen auf den ersten Blick ziemlich ähnlich aus, aber wenn es um die Implementierung geht, wird es interessant ...
Während mit einfachen Rückrufimplementierung ähnlich aussehen würde:
api.getUserPhoto(photoId, new Callback<Photo>() {
@Override
public void onSuccess() {
}
});
Das ist ganz einfach und unkompliziert. Und damit wird Observable
es schnell ausführlich und ziemlich kompliziert.
public Observable<Photo> getUserPhoto(final int photoId) {
return Observable.create(new Observable.OnSubscribeFunc<Photo>() {
@Override
public Subscription onSubscribe(Observer<? super Photo> observer) {
try {
observer.onNext(api.getUserPhoto(photoId));
observer.onCompleted();
} catch (Exception e) {
observer.onError(e);
}
return Subscriptions.empty();
}
}).subscribeOn(Schedulers.threadPoolForIO());
}
Und das ist es nicht. Sie müssen noch so etwas tun:
Observable.from(photoIdArray)
.mapMany(new Func1<String, Observable<Photo>>() {
@Override
public Observable<Photo> call(Integer s) {
return getUserPhoto(s);
}
})
.subscribeOn(Schedulers.threadPoolForIO())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Photo>() {
@Override
public void call(Photo photo) {
//save photo?
}
});
Vermisse ich hier etwas? Oder ist dies ein falscher Fall, um Observable
s zu verwenden ? Wann würde / sollte man einen Observable
einfachen Rückruf vorziehen ?
Aktualisieren
Die Nachrüstung ist viel einfacher als im obigen Beispiel, wie @Niels in seiner Antwort oder in Jake Whartons Beispielprojekt U2020 gezeigt hat . Aber im Wesentlichen bleibt die Frage dieselbe - wann sollte man die eine oder andere Art verwenden?
Antworten:
Für einfache Netzwerkaufgaben sind die Vorteile von RxJava gegenüber Callback sehr begrenzt. Das einfache getUserPhoto-Beispiel:
RxJava:
Zurückrufen:
Die RxJava-Variante ist nicht viel besser als die Callback-Variante. Lassen Sie uns vorerst die Fehlerbehandlung ignorieren. Machen wir eine Liste mit Fotos:
RxJava:
Zurückrufen:
Jetzt ist die RxJava-Variante immer noch nicht kleiner, obwohl sie mit Lambdas der Callback-Variante näher kommen würde. Wenn Sie Zugriff auf den JSON-Feed haben, ist es außerdem seltsam, alle Fotos abzurufen, wenn Sie nur die PNGs anzeigen. Passen Sie einfach den Feed an, um nur PNGs anzuzeigen.
Erste Schlussfolgerung
Dadurch wird Ihre Codebasis nicht kleiner, wenn Sie einen einfachen JSON laden, den Sie für das richtige Format vorbereitet haben.
Lassen Sie uns die Dinge jetzt etwas interessanter machen. Angenommen, Sie möchten nicht nur das userPhoto abrufen, sondern haben auch einen Instagram-Klon und möchten 2 JSONs abrufen: 1. getUserDetails () 2. getUserPhotos ()
Sie möchten diese beiden JSONs parallel laden. Wenn beide geladen sind, sollte die Seite angezeigt werden. Die Rückrufvariante wird etwas schwieriger: Sie müssen 2 Rückrufe erstellen, die Daten in der Aktivität speichern und, wenn alle Daten geladen sind, die Seite anzeigen:
Zurückrufen:
RxJava:
Wir kommen irgendwohin! Der Code von RxJava ist jetzt so groß wie die Rückrufoption. Der RxJava-Code ist robuster. Überlegen Sie, was passieren würde, wenn ein dritter JSON geladen werden müsste (wie die neuesten Videos)? Der RxJava müsste nur geringfügig angepasst werden, während die Rückrufvariante an mehreren Stellen angepasst werden muss (bei jedem Rückruf müssen wir überprüfen, ob alle Daten abgerufen wurden).
Ein anderes Beispiel; Wir möchten ein Autocomplete-Feld erstellen, das Daten mit Retrofit lädt. Wir möchten nicht jedes Mal einen Webcall durchführen, wenn ein EditText ein TextChangedEvent hat. Bei schneller Eingabe sollte nur das letzte Element den Aufruf auslösen. Auf RxJava können wir den Entprellungsoperator verwenden:
Ich werde die Callback-Variante nicht erstellen, aber Sie werden verstehen, dass dies viel mehr Arbeit ist.
Fazit: RxJava ist außergewöhnlich gut, wenn Daten als Stream gesendet werden. Das Retrofit Observable überträgt alle Elemente im Stream gleichzeitig. Dies ist an sich im Vergleich zu Callback nicht besonders nützlich. Wenn jedoch mehrere Elemente zu unterschiedlichen Zeiten in den Stream verschoben werden und Sie zeitbezogene Aufgaben ausführen müssen, macht RxJava den Code viel wartbarer.
quelle
inputObservable
? Ich habe viele Probleme mit dem Entprellen und möchte etwas mehr über diese Lösung erfahren.RxView
,RxTextView
etc. , die für die verwendet werden könneninputObservable
.Das Observable-Zeug ist bereits in Retrofit erledigt, daher könnte der Code folgendermaßen aussehen:
quelle
Callback
Sie sich abmelden könnenObserveable
, um häufige Probleme im Lebenszyklus zu vermeiden.Im Fall von getUserPhoto () sind die Vorteile für RxJava nicht groß. Nehmen wir ein anderes Beispiel, wenn Sie alle Fotos für einen Benutzer erhalten, jedoch nur, wenn das Bild PNG ist und Sie keinen Zugriff auf den JSON haben, um die Filterung auf der Serverseite durchzuführen.
Jetzt gibt der JSON eine Liste der Fotos zurück. Wir ordnen sie einzelnen Elementen zu. Auf diese Weise können wir die Filtermethode verwenden, um Fotos zu ignorieren, die nicht PNG sind. Danach abonnieren wir und erhalten für jedes einzelne Foto einen Rückruf, einen errorHandler und einen Rückruf, wenn alle Zeilen abgeschlossen sind.
TLDR Punkt hier ist; Der Rückruf gibt Ihnen nur einen Rückruf für Erfolg und Misserfolg zurück. Mit dem RxJava Observable können Sie Karten erstellen, verkleinern, filtern und vieles mehr.
quelle
Mit rxjava können Sie mehr mit weniger Code erledigen.
Nehmen wir an, Sie möchten die Sofort-Suche in Ihrer App implementieren. Bei Rückrufen haben Sie sich Sorgen gemacht, die vorherige Anfrage abzubestellen und die neue zu abonnieren, die Orientierungsänderung selbst zu handhaben ... Ich denke, es ist viel Code und zu ausführlich.
Mit rxjava ist das sehr einfach.
Wenn Sie die sofortige Suche implementieren möchten, müssen Sie nur auf TextChangeListener warten und aufrufen
photoModel.setUserId(EditText.getText());
In der onCreate-Methode für Fragmente oder Aktivitäten abonnieren Sie das Observable, das photoModel.subscribeToPhoto () zurückgibt. Es gibt ein Observable zurück, das immer die vom neuesten Observable (Anfrage) ausgegebenen Elemente ausgibt.
Wenn PhotoModel beispielsweise ein Singleton ist, müssen Sie sich keine Gedanken über Orientierungsänderungen machen, da BehaviorSubject die letzte Serverantwort ausgibt, unabhängig davon, wann Sie sich anmelden.
Mit diesen Codezeilen haben wir eine sofortige Suche implementiert und Orientierungsänderungen behandelt. Denken Sie, dass Sie dies mit Rückrufen mit weniger Code implementieren können? Ich bezweifle das.
quelle
Wir gehen normalerweise mit der folgenden Logik vor:
quelle
Aufgrund der Beispiele und Schlussfolgerungen in anderen Antworten gibt es meines Erachtens keinen großen Unterschied für einfache Aufgaben in einem oder zwei Schritten. Callback ist jedoch einfach und unkompliziert. RxJava ist komplizierter und zu groß für einfache Aufgaben. Es gibt die dritte Lösung von: AbacusUtil . Lassen Sie mich die obigen Anwendungsfälle mit allen drei Lösungen implementieren: Callback, RxJava, CompletableFuture (AbacusUtil) mit Retrolambda :
Foto aus dem Netzwerk holen und auf dem Gerät speichern / anzeigen:
Laden Sie Benutzerdetails und Foto parallel
quelle
Ich persönlich bevorzuge die Verwendung von Rx, um API-Antworten zu erhalten, wenn ich Filter, Map oder ähnliches für die Daten durchführen muss oder wenn ich andere API-Anrufe basierend auf früheren Anrufantworten ausführen muss
quelle
Es sieht so aus, als würden Sie das Rad neu erfinden. Was Sie tun, ist bereits in der Nachrüstung implementiert.
Als Beispiel könnten Sie sich RestAdapterTest.java von retrofit ansehen , wo sie eine Schnittstelle mit Observable als Rückgabetyp definieren und diese dann verwenden .
quelle
Wenn Sie eine App zum Spaß, ein Haustierprojekt oder einen POC oder den ersten Prototyp erstellen, verwenden Sie einfache Kernklassen für Android / Java wie Rückrufe, asynchrone Aufgaben, Loopers, Threads usw. Sie sind einfach zu verwenden und erfordern keine Lib-Integration von Drittanbietern. Eine große Bibliotheksintegration, nur um ein kleines, unveränderliches Projekt zu erstellen, ist unlogisch, wenn etwas Ähnliches von Anfang an getan werden kann.
Diese sind jedoch wie ein sehr scharfes Messer. Die Verwendung in einer Produktionsumgebung ist immer cool, hat aber auch Konsequenzen. Es ist schwierig, sicheren gleichzeitigen Code zu schreiben, wenn Sie mit Clean Coding und SOLID-Prinzipien nicht vertraut sind. Sie müssen eine ordnungsgemäße Architektur beibehalten, um zukünftige Änderungen zu erleichtern und die Teamproduktivität zu verbessern.
Auf der anderen Seite werden Parallelitätsbibliotheken wie RxJava, Co-Routinen usw. über eine Milliarde Mal erprobt und getestet, um produktionsbereiten gleichzeitigen Code zu schreiben. Auch hier ist es nicht so, dass Sie mit diesen Bibliotheken nicht gleichzeitig Code schreiben oder die gesamte Parallelitätslogik abstrahieren. Du bist immer noch. Aber jetzt ist es sichtbar und erzwingt ein klares Muster für das Schreiben von gleichzeitigem Code in der gesamten Codebasis und vor allem im gesamten Entwicklungsteam.
Dies ist ein Hauptvorteil der Verwendung eines Parallelitäts-Frameworks anstelle der einfachen alten Kernklassen, die sich mit roher Parallelität befassen. Versteht mich jedoch nicht falsch. Ich bin fest davon überzeugt, die Abhängigkeiten der externen Bibliothek zu begrenzen, aber in diesem speziellen Fall müssen Sie ein benutzerdefiniertes Framework für Ihre Codebasis erstellen. Dies ist eine zeitaufwändige Aufgabe und kann nur nach vorheriger Erfahrung ausgeführt werden. Daher werden Parallelitäts-Frameworks der Verwendung einfacher Klassen wie Rückrufe usw. vorgezogen.
TL'DR
Wenn Sie RxJava bereits für die gleichzeitige Codierung in der gesamten Codebasis verwenden, verwenden Sie einfach RxJava Observable / Flowable. Eine kluge Frage ist dann, ob ich Observables to Flowables verwenden soll. Wenn nicht, verwenden Sie einen Callable.
quelle