Share
Embed
RxJava est mort, vive RxJava 2 !
La migration de RxJava 2 à commencé en 2016. Mais 2017 sera sont année phare et va pouvoir profiter du chemin déjà tracé par sa précédente version.
Quels sont les changements apportés par cette nouvelle version, en terme d’API ou de performances ? En quoi RxJava et RxJava 2 sont-ils différents ? Faut-il migrer sur RxJava 2 tout de suite ?
Cette conférence fera un bref historique des ces bibliothèques avant de répondre à ces différentes questions.
(Android Makers - 10 Avril Paris 2017)
David
April 10, 2017
Transcript
202 slides
-
Future ExecutorService ex = Executors.newCachedThreadPool(); Future<String> future = ex.submit(() ->
longProcessing()); String result = future.get(); Appel bloquant -
Future Future<?> future1 = /* ... */ Future<?> future2 =
/* ... */ Future<?> future3 = /* ... */ Future<?> future4 = /* ... */ Future<?> future5 = /* ... */ Orchestration optimale ? -
Callback RemoteService service = buildRemoteService(); service.getUser(id -> { service.getData(id, data
-> { service.getSomething(data, whut -> { service.neverEndingCallBack(whut, () -> { }); }); }); }); Callback Hell -
Il y a toujours un truc qui ne marche pas
ça m’énerve ! -
Ça ne marche pas ! ça a planté ? dans
le doute, reboot -
API pour manipuler des événements de manière synchrone ou
asynchrone à travers un flux d’événements -
RxView.clicks(btn) .doOnNext(evt -> rotation.playFromStart()) .observeOn(Schedulers.io()) .switchMap(evt -> remoteApi.getData()) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(evt
-> { rotation.stop(); btn.setRotate(0); }) .doOnNext(value -> btn.setText("Data: " + value)) .subscribe(); Écoute des clicks Démarrage animation Appel web service Arrêt de l’animation Mise à jour de l’UI Abonnement -
Création des Reactive Extensions Création de RxJava Participation active
Reprise de RxJava & RxJava 2 Création de Reactor Participation à Reactor -
L’API Reactive Streams est un pont entre les
implémentations -
RxJava n’est pas compatible avec Reactive Streams (il faut
alors utiliser un adapteur : RxJavaReactiveStreams) https://github.com/ReactiveX/RxJavaReactiveStreams -
onNext * ( onError | onComplete ) onNext *
( onError | onCompleted ) RxJava Reactive Streams -
onNext * ( onError | onComplete ) onNext *
( onError | onCompleted ) RxJava Reactive Streams Nom différent -
RxJava Contrat Backpressure Observable [N] Oui Single [1] Non Completable
[0] Non Ajouté après coup Appel Web service Processus d’arrière plan -
Écouter une websocket, pour c h a q u e
c o m m a n d e r e ç u e , composer une réponse à partir de 3 webservices différents, puis exécuter 2 jobs séquentiellement ? -
Écouter une websocket, pour c h a q u e
c o m m a n d e r e ç u e , composer une réponse à partir de 3 webservices différents, puis exécuter 2 jobs séquentiellement ? -
Écouter une websocket, pour c h a q u e
c o m m a n d e r e ç u e , composer une réponse à partir de 3 webservices différents, puis exécuter 2 jobs séquentiellement ? -
Écouter une websocket, pour c h a q u e
c o m m a n d e r e ç u e , composer une réponse à partir de 3 webservices différents, puis exécuter 2 jobs séquentiellement ? -
websocket("/topics/cmd") .observeOn(Schedulers.io()) .switchMap(cmd -> Single.zip( api.getActions(), api.getScore(), api.getUserData(), this::composeResult).toObservable()) .observeOn(Schedulers.computation())
.concatMap(result -> updateDb(result).andThen(getLastResults())) .subscribe(last -> System.out.println("last results -> " + last)); -
websocket("/topics/cmd") .observeOn(Schedulers.io()) .switchMap(cmd -> Single.zip( api.getActions(), api.getScore(), api.getUserData(), this::composeResult).toObservable()) .observeOn(Schedulers.computation())
.concatMap(result -> updateDb(result).andThen(getLastResults())) .subscribe(last -> System.out.println("last results -> " + last)); Écoute de la websocket Observable -
websocket("/topics/cmd") .observeOn(Schedulers.io()) .switchMap(cmd -> Single.zip( api.getActions(), api.getScore(), api.getUserData(), this::composeResult).toObservable()) .observeOn(Schedulers.computation())
.concatMap(result -> updateDb(result).andThen(getLastResults())) .subscribe(last -> System.out.println("last results -> " + last)); Composition d’une réponse à partir de 3 webservices Single -
websocket("/topics/cmd") .observeOn(Schedulers.io()) .switchMap(cmd -> Single.zip( api.getActions(), api.getScore(), api.getUserData(), this::composeResult).toObservable()) .observeOn(Schedulers.computation())
.concatMap(result -> updateDb(result).andThen(getLastResults())) .subscribe(last -> System.out.println("last results -> " + last)); Completable Exécuter 2 jobs -
websocket("/topics/cmd") .observeOn(Schedulers.io()) .switchMap(cmd -> Single.zip( api.getActions(), api.getScore(), api.getUserData(), this::composeResult).toObservable()) .observeOn(Schedulers.computation())
.concatMap(result -> updateDb(result).andThen(getLastResults())) .subscribe(last -> System.out.println("last results -> " + last)); Écoute de la websocket Observable Composition d’une réponse à partir de 3 webservices Single Completable Exécuter 2 jobs -
RxJava 2 Contrat Backpressure Observable [N] Non Single [1] Non
Completable [0] Non Maybe [0|1] Non Nouveau ! Concept « similaire » à Optional -
acceptedIntent .filter(intent -> !intent.getBooleanExtra("UpdatePhoneMode", false)) .concatMap(intent -> approximatedEngine.detectCurrentPlace()) .doOnNext(score ->
Log.info(TAG, "Scan completed with result " + score)) .concatMap(this::detectSleepMode) .concatMap((score) -> isNewPlace(score.getScore().getPlace()).map(p -> score)) .doOnNext((p) -> Log.info(TAG, "Current place found is : " + p)) .subscribe() -
acceptedIntent .filter(intent -> !intent.getBooleanExtra("UpdatePhoneMode", false)) .onBackpressureDrop() .concatMap(intent -> approximatedEngine.detectCurrentPlace()) .doOnNext(score
-> Log.info(TAG, "Scan completed with result " + score)) .onBackpressureDrop() .concatMap(this::detectSleepMode) .onBackpressureDrop() .concatMap((score) -> isNewPlace(score.getScore().getPlace()).map(p -> score)) .doOnNext((p) -> Log.info(TAG, "Current place found is : " + p)) .subscribe() Ajouté dans un moment de panique -
RxJava 2 Contrat Backpressure Observable [N] Non Single [1] Non
Completable [0] Non Maybe [0|1] Non Nouveau ! Concept « similaire » à Optional -
RxJava 2 Contrat Backpressure Observable [N] Non Single [1] Non
Completable [0] Non Maybe [0|1] Non Flowable [N] Oui Nouveau ! Observable avec back pressure Concept « similaire » à Optional -
Observable → moins de 1000 éléments → Gestion d’élément d’interface
graphique → Remplacement de Stream Java Flowable → plus de 10 000 éléments → contrôle de l’émission de données → Flux réseau avec contrôle de débit -
P o u v o i r u t i
l i s e r u n e biblio t hèq ue utilisant Reactive Streams dans son projet RxJava 2 (et vice versa) -
all amb ambArray ambWith any as awaitOnSubscribe blockFirst blockFirstMillis blockLast
blockLastMillis blockingFirst blockingForEach blockingIterable blockingLast blockingLatest blockingMostRecent blockingNext blockingSingle blockingSubscribe buffer bufferMillis bufferSize bufferTimeout bufferTimeoutMillis bufferUntil bufferWhile cache cacheWithInitialCapacity cancelOn cast checkpoint collect collectInto collectList collectMap collectMultimap collectSortedList combineLatest combineLatestDelayError compose concat concatArray concatArrayDelayError concatArrayEager concatDelayError concatEager concatMap concatMapDelayError concatMapEager concatMapEagerDelayError concatMapIterable concatWith contains count create debounce defaultIfEmpty defer delay delayElements delayElementsMillis delayMillis delaySubscription delaySubscriptionMillis dematerialize distinct distinctUntilChanged doAfterNext doAfterTerminate doFinally doOnCancel doOnComplete doOnEach doOnError doOnLifecycle doOnNext doOnRequest doOnSubscribe doOnTerminate elapsed elementAt elementAtOrError empty equals error filter first firstElement firstEmitting firstEmittingWith firstOrError flatMap flatMapCompletable flatMapIterable flatMapMaybe flatMapSequential flatMapSingle forEach forEachWhile from fromArray fromCallable fromFuture fromIterable fromPublisher fromStream generate getClass getPrefetch groupBy groupJoin handle hasElement hasElements hashCode hide ignoreElements interval intervalMillis intervalRange isEmpty join just last lastElement lastOrError lift limitRate log map mapError materialize merge mergeArray mergeArrayDelayError mergeDelayError mergeSequential mergeWith never next notify notifyAll observeOn ofType onBackpressureBuffer onBackpressureDrop onBackpressureError onBackpressureLatest onErrorResumeNext onErrorResumeWith onErrorReturn onErrorReturnItem onExceptionResumeNext onTerminateDetach parallel publish publishNext publishOn range rangeLong rebatchRequests reduce reduceWith repeat repeatUntil repeatWhen replay replayMillis retry retryUntil retryWhen safeSubscribe sample sampleFirst sampleFirstMillis sampleMillis sampleTimeout scan scanWith sequenceEqual serialize share single singleElement singleOrEmpty singleOrError skip skipLast skipMillis skipUntil skipUntilOther skipWhile sort sorted startWith startWithArray strict subscribe subscribeOn subscribeWith switchIfEmpty switchMap switchMapDelayError switchOnError switchOnNext switchOnNextDelayError take takeLast takeMillis takeUntil takeUntilOther takeWhile test then thenEmpty thenMany throttleFirst throttleLast throttleWithTimeout timeInterval timeout timeoutMillis timer timestamp to toFuture toIterable toList toMap toMultimap toObservable toSortedList toStream toString transform unsafeCreate unsubscribeOn using wait window windowMillis windowTimeout windowTimeoutMillis windowUntil windowWhile withLatestFrom zip zipArray zipIterable zipWith zipWithIterable -
RxJava RxJava 2 flatMap flatMap Émission d’aucune, une ou plusieurs
valeurs amb amb Émission des valeurs du flux répondant en premier … … … debounce debounce Ignore les événements pendant un laps de temps -
RxJava RxJava 2 Observable.fromAsync Observable.create Observable<T> first() Maybe<T> firstElement() Observable<T>
firstOrDefault(T) Single<T> first(T) Observable<T> ignoreElements() Completable ignoreElements() -
RxJava RxJava 2 Observable.fromAsync Observable.create Observable<T> first() Maybe<T> firstElement() Observable<T>
firstOrDefault(T) Single<T> first(T) Observable<T> ignoreElements() Completable ignoreElements() -
RxJava RxJava 2 Observable.fromAsync Observable.create Observable<T> first() Maybe<T> firstElement() Observable<T>
firstOrDefault(T) Single<T> first(T) Observable<T> ignoreElements() Completable ignoreElements() -
RxJava RxJava 2 Observable.fromAsync Observable.create Observable<T> first() Maybe<T> firstElement() Observable<T>
firstOrDefault(T) Single<T> first(T) Observable<T> ignoreElements() Completable ignoreElements() -
RxJava RxJava 2 Observable.fromAsync Observable.create Observable<T> first() Maybe<T> firstElement() Observable<T>
firstOrDefault(T) Single<T> first(T) Observable<T> ignoreElements() Completable ignoreElements() -
https://github.com/ReactiveX/RxJava/wiki/Implementing-custom-operators-(draft) writing operators is hard when one writes an operator,
the Observable protocol, unsubscription, backpressure and concurrency have to be taken into account and adhered to the letter -
Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler
(Pool de Thread) -
Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler
(Pool de Thread) -
Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler
(Pool de Thread) -
Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler
(Pool de Thread) -
Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler
(Pool de Thread) -
Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler
(Pool de Thread) -
Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler
(Pool de Thread) -
Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler
(Pool de Thread) -
RxView.clicks(btn) .observeOn(Schedulers.io()) .switchMap(evt -> remoteApi.getData()) .observeOn(Schedulers.computation()) .flatMap(data -> intensiveComputation(data))
.observeOn(AndroidSchedulers.mainThread()) .doOnNext(value -> btn.setText("Data: " + value)) .subscribe(); -
RxView.clicks(btn) .observeOn(Schedulers.io()) .switchMap(evt -> remoteApi.getData()) .observeOn(Schedulers.computation()) .flatMap(data -> intensiveComputation(data))
.observeOn(AndroidSchedulers.mainThread()) .doOnNext(value -> btn.setText("Data: " + value)) .subscribe(); entrée/sortie calculs Thread graphique -
RxJava RxJava 2 Description io() io() Pool de Thread qui
grossit au besoin computation() computation() Pool fini de Thread single() single() Pool de 1 Thread immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer -
RxJava RxJava 2 Description io() io() Pool de Thread qui
grossit au besoin computation() computation() Pool fini de Thread single() single() Pool de 1 Thread immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer -
RxJava RxJava 2 Description io() io() Pool de Thread qui
grossit au besoin computation() computation() Pool fini de Thread single() single() Pool de 1 Thread immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer -
RxJava RxJava 2 Description io() io() Pool de Thread qui
grossit au besoin computation() computation() Pool fini de Thread single() single() Pool de 1 Thread immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer -
RxJava RxJava 2 Description io() io() Pool de Thread qui
grossit au besoin computation() computation() Pool fini de Thread single() single() Pool de 1 Thread immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer Absent pour votre bien -
RxJava RxJava 2 Description io() io() Pool de Thread qui
grossit au besoin computation() computation() Pool fini de Thread single() single() Pool de 1 Thread immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer -
Flowable.range(1, 100000) .subscribe(new Subscriber<Integer>() { private Subscription source;
@Override public void onSubscribe(Subscription s) { this.source = s; this.source.request(1); } @Override public void onNext(Integer o) { longComputation(o); this.source.request(1); } }); -
Flowable.range(1, 100000) .subscribe(new Subscriber<Integer>() { private Subscription source;
@Override public void onSubscribe(Subscription s) { this.source = s; this.source.request(1); } @Override public void onNext(Integer o) { longComputation(o); this.source.request(1); } }); Garder la référence de l’abonnement Demander l’émission des éléments suivants Demande l’émission des premiers éléments -
Flowable.range(1, 100000) .subscribe(new Subscriber<Integer>() { private Subscription source;
@Override public void onSubscribe(Subscription s) { this.source = s; this.source.request(1); } @Override public void onNext(Integer o) { longComputation(o); this.source.request(1); } }); Reactive Streams -
Flowable.range(1, 100000) .subscribe(new Subscriber<Integer>() { private Subscription source;
@Override public void onSubscribe(Subscription s) { this.source = s; this.source.request(1); } @Override public void onNext(Integer o) { longComputation(o); this.source.request(1); } }); Contrôle de l’émission -
source() .subscribe(new Subscriber<Integer>() { private Subscription source; @Override
public void onSubscribe(Subscription s) { this.source = s; this.source.request(1); } @Override public void onNext(Integer o) { longComputation(o); this.source.request(1); } }); Update GPS Liste déroulante -
-Jake Wharton « Unless you can model your entire system
synchronously, a single asynchronous source breaks imperative programming » -
-Jake Wharton « Unless you can model your entire system
synchronously, a single asynchronous source breaks imperative programming » -
RxJava RxJava 2 Flowable.just Émission d’une valeur existante Flowable.defer Émission
tardive d’un Observable Flowable.fromCallable Émission à partir d’un appel de fonction Flowable.create Contrôle manuel de l’émission des événements Flowable.using Gestion d’une ressource Flowable.fromPublisher Utilisation d’un Publisher (Reactive Streams) Flowable.generate Utilisation d’un générateur de valeurs -
RxJava RxJava 2 Flowable.just Émission d’une valeur existante Flowable.defer Émission
tardive d’un Observable Flowable.fromCallable Émission à partir d’un appel de fonction Flowable.create Contrôle manuel de l’émission des événements Flowable.using Gestion d’une ressource Flowable.fromPublisher Utilisation d’un Publisher (Reactive Streams) Flowable.generate Utilisation d’un générateur de valeurs -
RxJava RxJava 2 Flowable.just Émission d’une valeur existante Flowable.defer Émission
tardive d’un Observable Flowable.fromCallable Émission à partir d’un appel de fonction Flowable.create Contrôle manuel de l’émission des événements Flowable.using Gestion d’une ressource Flowable.fromPublisher Utilisation d’un Publisher (Reactive Streams) Flowable.generate Utilisation d’un générateur de valeurs -
RxJava RxJava 2 Flowable.just Émission d’une valeur existante Flowable.defer Émission
tardive d’un Observable Flowable.fromCallable Émission à partir d’un appel de fonction Flowable.create Contrôle manuel de l’émission des événements Flowable.using Gestion d’une ressource Flowable.fromPublisher Utilisation d’un Publisher (Reactive Streams) Flowable.generate Utilisation d’un générateur de valeurs -
RxJava RxJava 2 Flowable.just Émission d’une valeur existante Flowable.defer Émission
tardive d’un Observable Flowable.fromCallable Émission à partir d’un appel de fonction Flowable.create Contrôle manuel de l’émission des événements Flowable.using Gestion d’une ressource Flowable.fromPublisher Utilisation d’un Publisher (Reactive Streams) Flowable.generate Utilisation d’un générateur de valeurs -
Observable.<Event>create(emitter -> { Callback listener = new Callback() { @Override
public void onEvent(Event e) { emitter.onNext(e); if (e.isLast()) { emitter.onComplete(); } } @Override public void onFailure(Exception e) { emitter.onError(e); } }; AutoCloseable c = api.someMethod(listener); emitter.setCancellation(c::close); }); Contract RxJava Contract RxJava Désabonnement -
Flowable.<Event>create(emitter -> { Callback listener = new Callback() { @Override
public void onEvent(Event e) { emitter.onNext(e); if (e.isLast()) { emitter.onComplete(); } } @Override public void onFailure(Exception e) { emitter.onError(e); } }; AutoCloseable c = api.someMethod(listener); emitter.setCancellation(c::close); }, BackpressureStrategy.BUFFER); Backpressure -
RxJava 2 Observable.just(null); Single.just(null); Observable.fromCallable(() -> null) .subscribe(System.out::println,
Throwable::printStackTrace); Observable.just(1).map(v -> null) .subscribe(System.out::println, Throwable::printStackTrace); NullPointerException NullPointerException NullPointerException N ullPointerException -
Observable.range(1, 10) .map(i -> { if (i % 2 ==
0) { return null; } else { return i; } }) .filter(i -> i != null) .subscribe(System.out::println); émission de null filtre de null RxJava -
Observable.range(1, 10) .flatMap(i -> { if (i % 2 ==
0) { return Observable.empty(); } else { return Observable.just(i); } }) .subscribe(System.out::println); émission de empty RxJava 2 -
Disposable abonnement = observeClicks() .map(evt -> "click !") .subscribe();
CompositeDisposable abonnements = new CompositeDisposable(abonnement); abonnements.clear(); Subscription → Disposable CompositeSubscription → CompositeDisposable -
rx.Action1 rx.Func1 RxJava RxJava 2 rx.Func2 rx.Func3 rx.Action2 io.reactivex.functions.Consumer io.reactivex.functions.Function
io.reactivex.functions.BiFunction io.reactivex.functions.Function3 io.reactivex.functions.BiConsumer Suivre les conventions Java 8 -
RxJavaInterop.toV2Observable(rx.Observable.just(1, 2, 3)) .firstElement() .subscribe(); RxJavaInterop.toV1Observable(io.reactivex.Observable.just(1, 2, 3),
BackpressureStrategy.DROP) .first() .subscribe(); https://github.com/akarnokd/RxJava2Interop RxJava → RxJava 2 RxJava 2 → RxJava -
public class AndroidApp extends Application { static {
RxJavaPlugins.getInstance().registerErrorHandler(new RxJavaErrorHandler() { @Override public void handleError(Throwable e) { e.printStackTrace(); Log.error("RX-EXCEPTION", "An Stream throw an exception ", e); } }); } } Doit être défini en 1er Gestion d’erreur par défaut -
rx.exceptions.OnErrorNotImplementedException: Sequence contains too many elements at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java: 386) at
rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java: 383) at rx.internal.util.ActionSubscriber.onError(ActionSubscriber.java:44) at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:153) at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:115) at rx.internal.operators.OperatorSingle$ParentSubscriber.onNext(OperatorSingle.java:97) at rx.internal.operators.OnSubscribeRange$RangeProducer.slowPath(OnSubscribeRange.java:90) at rx.internal.operators.OnSubscribeRange$RangeProducer.request(OnSubscribeRange.java:68) at rx.Subscriber.setProducer(Subscriber.java:211) at rx.internal.operators.OnSubscribeRange.call(OnSubscribeRange.java:38) Stacktrace inexploitable -
RxJavaHooks.enableAssemblyTracking(); […] Caused by: rx.exceptions.AssemblyStackTraceException: Assembly trace: at rx.Observable.create(Observable.java:100) at
rx.Observable.lift(Observable.java:237) at rx.Observable.single(Observable.java:9330) at rxjava2.BasicExampleTest.stacktrace(BasicExampleTest.java:225) rx.Observable.range(1, 100) .single() .subscribe(); -
enableTracing n’est pas encore supporté par RxJava 2 (pour cela,
il faut utiliser RxJava2Extensions) https://github.com/akarnokd/RxJava2Extensions -
for (int x = 0; x < 10_000; x++) {
Observable.interval(10, TimeUnit.MILLISECONDS) .takeWhile(i -> take.get()) .flatMap(i -> Observable.range(1, 100)) .subscribe(); } -
We are aggressively migrating our internal code to RxJava
2 https://github.com/uber/AutoDispose -
Merci pour votre attention On reste en contact ? [email protected]
@dwursteisen http://blog.soat.fr RefCard RxJava 2 bientôt disponible !