Pracuję nad siecią dla mojej aplikacji. Postanowiłem więc wypróbować Square's Retrofit . Widzę, że obsługują prosteCallback
@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);
i RxJava Observable
@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);
Oba wyglądają dość podobnie na pierwszy rzut oka, ale kiedy dochodzi do implementacji, robi się interesująco ...
Podczas gdy z prostą implementacją wywołania zwrotnego wyglądałoby podobnie do tego:
api.getUserPhoto(photoId, new Callback<Photo>() {
@Override
public void onSuccess() {
}
});
co jest dość proste i jednoznaczne. A dzięki Observable
temu szybko staje się gadatliwy i dość skomplikowany.
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());
}
I to nie wszystko. Nadal musisz zrobić coś takiego:
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?
}
});
Czy coś mi umyka? Czy jest to niewłaściwy przypadek użycia Observable
s? Kiedy należy / należy preferować Observable
proste wywołanie zwrotne?
Aktualizacja
Korzystanie z modernizacji jest znacznie prostsze niż powyższy przykład, jak pokazał @Niels w swojej odpowiedzi lub w przykładowym projekcie Jake'a Whartona U2020 . Ale w gruncie rzeczy pytanie pozostaje takie samo - kiedy należy użyć jednego lub drugiego sposobu?
Odpowiedzi:
W przypadku prostych rzeczy sieciowych przewaga RxJava nad wywołaniem zwrotnym jest bardzo ograniczona. Prosty przykład getUserPhoto:
RxJava:
Oddzwonić:
Wariant RxJava nie jest dużo lepszy niż wariant Callback. Na razie zignorujmy obsługę błędów. Zróbmy listę zdjęć:
RxJava:
Oddzwonić:
Teraz wariant RxJava wciąż nie jest mniejszy, chociaż w przypadku Lambdas byłby on coraz bliżej wariantu Callback. Ponadto, jeśli masz dostęp do kanału JSON, byłoby dziwnie odzyskać wszystkie zdjęcia, gdy wyświetlasz tylko pliki PNG. Wystarczy dostosować kanał, aby wyświetlał tylko pliki PNG.
Pierwszy wniosek
Nie zmniejsza to bazy kodu, gdy ładujesz prosty JSON, który przygotowałeś do właściwego formatu.
Teraz sprawmy, by było trochę ciekawiej. Powiedzmy, że nie tylko chcesz odzyskać zdjęcie użytkownika, ale masz klon na Instagramie i chcesz pobrać 2 JSON: 1. getUserDetails () 2. getUserPhotos ()
Chcesz załadować te dwa JSON równolegle, a gdy oba zostaną załadowane, strona powinna zostać wyświetlona. Wariant wywołania zwrotnego stanie się nieco trudniejszy: musisz utworzyć 2 połączenia zwrotne, zapisać dane w działaniu, a jeśli wszystkie dane są załadowane, wyświetl stronę:
Oddzwonić:
RxJava:
Dostajemy gdzieś! Kod RxJava jest teraz tak duży, jak opcja wywołania zwrotnego. Kod RxJava jest bardziej niezawodny; Pomyśl, co by się stało, gdybyśmy potrzebowali załadować trzeciego JSON (jak najnowsze filmy)? RxJava wymagałby tylko niewielkiej korekty, podczas gdy wariant oddzwaniania musi być dostosowany w wielu miejscach (przy każdym oddzwanianiu musimy sprawdzić, czy wszystkie dane są pobierane).
Inny przykład; chcemy utworzyć pole autouzupełniania, które ładuje dane za pomocą Retrofit. Nie chcemy robić połączenia za każdym razem, gdy EditText ma TextChangedEvent. Podczas szybkiego pisania tylko ostatni element powinien wyzwalać połączenie. Na RxJava możemy użyć operatora debounce:
Nie stworzę wariantu oddzwaniania, ale zrozumiecie, że to dużo więcej pracy.
Wniosek: RxJava jest wyjątkowo dobry, gdy dane są wysyłane jako strumień. Retrofit Observable popycha wszystkie elementy w strumieniu w tym samym czasie. To nie jest szczególnie przydatne samo w sobie w porównaniu do oddzwaniania. Ale kiedy w strumieniu jest wiele elementów wypychanych i w różnych czasach, i musisz robić rzeczy związane z taktowaniem, RxJava sprawia, że kod jest o wiele łatwiejszy w utrzymaniu.
źródło
inputObservable
? Mam wiele problemów z ogłaszaniem i chciałbym dowiedzieć się nieco więcej o tym rozwiązaniu.RxView
,RxTextView
etc, które mogą być wykorzystane doinputObservable
.Obserwowalne rzeczy są już zrobione w ramach Retrofit, więc kod może wyglądać tak:
źródło
Callback
których można zrezygnowaćObserveable
, unikając typowych problemów w cyklu życia.W przypadku getUserPhoto () zalety RxJava nie są świetne. Ale weźmy inny przykład, gdy dostaniesz wszystkie zdjęcia dla użytkownika, ale tylko wtedy, gdy obraz jest w formacie PNG i nie masz dostępu do JSON, aby wykonać filtrowanie po stronie serwera.
Teraz JSON zwraca listę zdjęć. Spłaszczymy je do poszczególnych elementów. W ten sposób będziemy mogli użyć metody filtrowania, aby zignorować zdjęcia, które nie są PNG. Następnie zasubskrybujemy i otrzymamy oddzwonienie dla każdego pojedynczego zdjęcia, moduł obsługi błędów i oddzwonienie, gdy wszystkie wiersze zostaną zakończone.
Punktem tutaj jest TLDR ; oddzwonienie zwraca tylko oddzwonienie w przypadku sukcesu i niepowodzenia; RxJava Observable pozwala robić mapy, redukować, filtrować i wiele innych rzeczy.
źródło
Z rxjava możesz robić więcej rzeczy przy mniejszym kodzie.
Załóżmy, że chcesz wdrożyć natychmiastowe wyszukiwanie w swojej aplikacji. W przypadku wywołań zwrotnych martwisz się o rezygnację z subskrypcji poprzedniej prośby i zasubskrybowanie nowej, samodzielna obsługa orientacji ... Myślę, że to dużo kodu i jest zbyt szczegółowy.
Z rxjava jest bardzo prosta.
jeśli chcesz wdrożyć natychmiastowe wyszukiwanie, musisz tylko nasłuchiwać TextChangeListener i wywoływać
photoModel.setUserId(EditText.getText());
W metodzie fragmentu lub aktywności onCreate subskrybujesz Observable, która zwraca photoModel.subscribeToPhoto (), zwraca Observable, które zawsze emituje elementy emitowane przez najnowszą Observable (żądanie).
Ponadto, jeśli PhotoModel jest na przykład Singletonem, nie musisz się martwić o zmiany orientacji, ponieważ BehaviorSubject emituje ostatnią odpowiedź serwera, niezależnie od tego, kiedy subskrybujesz.
Dzięki tym wierszom kodu zaimplementowaliśmy natychmiastowe wyszukiwanie i obsługujemy zmiany orientacji. Czy uważasz, że możesz to zaimplementować za pomocą wywołań zwrotnych z mniejszym kodem? Wątpię.
źródło
Zwykle stosujemy następującą logikę:
źródło
Na podstawie próbek i wniosków z innych odpowiedzi uważam, że nie ma dużej różnicy dla prostych zadań jedno- lub dwuetapowych. Oddzwonienie jest jednak proste i jednoznaczne. RxJava jest bardziej skomplikowany i zbyt duży, by wykonać proste zadanie. Istnieje trzecie rozwiązanie: AbacusUtil . Pozwól mi wdrożyć powyższe przypadki użycia ze wszystkimi trzema rozwiązaniami: Callback, RxJava, CompletableFuture (AbacusUtil) z Retrolambda :
Pobierz zdjęcie z sieci i zapisz / wyświetl na urządzeniu:
Załaduj dane użytkownika i zdjęcie równolegle
źródło
Ja osobiście wolę używać Rx, aby uzyskać odpowiedzi interfejsu API w przypadkach, w których muszę wykonać filtr, mapę lub coś takiego na danych Lub w przypadkach, gdy muszę wykonać inne wywołania interfejsu API na podstawie odpowiedzi na poprzednie połączenia
źródło
Wygląda na to, że zmieniasz kierownicę, a to, co robisz, zostało już zaimplementowane w ramach modernizacji.
Na przykład, możesz spojrzeć na RestAdapterTest.java modernizacji , gdzie definiują interfejs z Observable jako typem powrotu, a następnie go użyć .
źródło
Podczas tworzenia aplikacji dla zabawy, projektu zwierzaka, POC lub pierwszego prototypu korzystasz z prostych podstawowych klas Androida / Java, takich jak wywołania zwrotne, zadania asynchroniczne, pętle, wątki itp. Są one łatwe w użyciu i nie wymagają żadnych Integracja lib stron trzecich. Duża integracja bibliotek tylko po to, by zbudować niewielki, niezmienny projekt, jest nielogiczna, gdy coś podobnego można zrobić od razu.
Są jednak jak bardzo ostry nóż. Używanie ich w środowisku produkcyjnym jest zawsze fajne, ale także będą miały konsekwencje. Trudno jest pisać bezpieczny współbieżny kod, jeśli nie znasz dobrze zasad Czystego Kodowania i zasad SOLID. Musisz zachować odpowiednią architekturę, aby ułatwić przyszłe zmiany i poprawić wydajność zespołu.
Z drugiej strony biblioteki współbieżności, takie jak RxJava, wspólne procedury itp. Są testowane i testowane ponad miliard razy, aby pomóc w pisaniu gotowego do produkcji współbieżnego kodu. Znowu nie jest tak, że przy użyciu tych bibliotek nie piszesz współbieżnego kodu ani nie wyodrębniasz całej logiki współbieżności. Nadal jesteś. Ale teraz jest widoczny i wymusza wyraźny wzorzec do pisania współbieżnego kodu w całej bazie kodu, a co ważniejsze w całym zespole programistycznym.
Jest to główna zaleta korzystania z frameworku współbieżności zamiast zwykłych starych klas podstawowych, które zajmują się surową współbieżnością. Nie zrozumcie mnie jednak źle. Jestem wielkim zwolennikiem ograniczenia zależności biblioteki zewnętrznej, ale w tym konkretnym przypadku będziesz musiał zbudować niestandardową strukturę dla swojej bazy kodu, co jest czasochłonnym zadaniem i może być wykonane tylko po wcześniejszym doświadczeniu. Dlatego frameworki współbieżności są bardziej preferowane niż używanie zwykłych klas, takich jak wywołania zwrotne itp.
TL'DR
Jeśli już używasz RxJava do współbieżnego kodowania w całej bazie kodu, po prostu użyj RxJava Observable / Flowable. Mądre pytanie, które należy zadać, to czy powinienem użyć Observables to Flowables. Jeśli nie, to idź dalej i użyj opcji na żądanie.
źródło