Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to Think, in RxJava, Before Reacting

How to Think, in RxJava, Before Reacting

This talk was given at GeeCON, 11-13 May 2016, Kraków, Poland (http://2016.geecon.org/). Link for the video is https://vimeo.com/170796165 .

RxJava is a library that helps in writing code using reactive paradigm. When writing programs using RxJava, the hardest part is thinking in Reactive, which requires one to let go of old imperative and stateful habits of programming, and start thinking in a different paradigm. At my work in ThoughtWorks I have been using RxJava for more that six months in production applications. My talk will be based on the knowledge that I gained and lessons that I learnt from my practical experience of RxJava.

My goals from this talk are:
(1) To demystify the reactive paradigms concepts for the attendees and
(2) Provide enough information about the mechanics of RxJava so that attendees can straight away start experimenting with the library

The topics that I will be talking about are:
(1) Introduction to Reactive Programming,
(2) Creating Observable (the source),
(3) Creating Observer (the consumer),
(4) Subscribing (connecting consumer to source),
(5) Operators (for Transforming, Filtering and Combining),
(6) Building Resilience (Error handling),
(7) Using Concurrency (Schedulers),
(8) Unit Testing Utilities

Praveer Gupta

May 13, 2016
Tweet

More Decks by Praveer Gupta

Other Decks in Technology

Transcript

  1. • Java VM implementation of ReactiveX • An API for

    asynchronous programming with observable streams What is Rx in RxJava? RxJs Rx.NET RxScala RxClojure RxSwift Rx.rb RxPY & Others
  2. • Frontend • Server Side Applications of RxJava Internal Processing

    Service A Service B Service C Request Total Request Processing Time Service Boundary Parallel Sequential Response Combine
  3. RxJava Observable Subscriber observeOn subscribeOn fromCallable timer interval map flatMap

    timestamp filter distinctUntilChanged just error onError onNext onCompleted take buffer zip combineLatest concat merge Scheduler io computation newThread Observer Pattern Reactive Functional Asynchronous Resiliency TestSubscriber TestScheduler
  4. How to think in RxJava fromCallable timer interval just error

    Observable Subscriber onError onNext onCompleted Observer Pattern Reactive map flatMap timestamp filter distinctUntilChanged take buffer zip combineLatest concat merge Functional observeOn subscribeOn Scheduler io computation newThread Asynchronous TestSubscriber TestScheduler Resiliency retry timeout onErrorResumeNext Observable Subscriber onError onNext onCompleted Observer Pattern Reactive Concept fromCallable timer interval just error Create map flatMap timestamp filter distinctUntilChanged take buffer zip combineLatest concat merge Functional Operate observeOn subscribeOn Scheduler io computation newThread Asynchronous Resiliency retry timeout onErrorResumeNext TestSubscriber TestScheduler Concurrent Handle errors Test
  5. Subscriber (Consumer) Observable (Source) 2 onNext( ) 2 2 1

    1 onNext( ) 1 Source Consumer subscribe Produces data/event asynchronously Consumes data/event as they arrive Pushes data/event Observer & Iterator Pattern
  6. onError( ) X X X Observable Subscriber (Source) (Consumer) unsubscribe

    Pushes error event Observer & Iterator Pattern Error
  7. onNext( ) 1 Observable Subscriber onError( ) X onCompleted() Pushing

    Streams of Data/Events Observer & Iterator Pattern
  8. Subscriber<Integer> subscriber = Subscribers.create(
 n -> System.out.println(n),
 e -> System.out.print(e.getClass()),


    () -> System.out.println("Completed")
 ); Observable<Integer> observable = Observable.just(1, 2, 3); observable.subscribe(subscriber); Subscriber<Integer> subscriber = Subscribers.create(
 n -> System.out.println(n),
 e -> System.out.print(e.getClass()),
 () -> System.out.println("Completed")
 ); Subscriber<Integer> subscriber = Subscribers.create(
 n -> System.out.println(n),
 e -> System.out.println("Error"),
 () -> System.out.println("Completed")
 ); Subscriber<Integer> subscriber = Subscribers.create(
 n -> System.out.println(n),
 e -> System.out.println("Error"),
 () -> System.out.println("Completed")
 ); Subscriber<Integer> subscriber = Subscribers.create(
 n -> System.out.println(n),
 e -> System.out.println("Error"),
 () -> System.out.println("Completed")
 ); Observable<Integer> observable = Observable.just(1, 2, 3); Subscriber<Integer> subscriber = Subscribers.create(
 n -> System.out.println(n),
 e -> System.out.println("Error"),
 () -> System.out.println("Completed")
 ); Observable<Integer> observable = Observable.just(1, 2, 3); RxJava in Action Source Consumer onNext onError onCompleted connect
  9. Observable.just(1, 2, 3).subscribe(
 n -> System.out.println(n),
 e -> System.out.println("Error"),
 ()

    -> System.out.println("Completed")
 ); 1 Output: 2 3 Completed RxJava in Action
  10. Observable.fromCallable(new Callable<String>() {
 @Override
 public String call() throws Exception {


    return "result";
 }
 }).subscribe( n -> System.out.println(n), e -> System.out.println(“Error"), () -> System.out.println("Completed") ); fromCallable Observable.fromCallable(new Callable<String>() {
 @Override
 public String call() throws Exception {
 return "result";
 }
 }).subscribe( n -> System.out.println(n), e -> System.out.println(“Error"), () -> System.out.println("Completed") );
  11. Observable.fromCallable(() -> "result"); .subscribe( s -> System.out.println(s), e -> System.out.println("Error"),

    () -> System.out.println("Completed") ); fromCallable Output: result Completed Observable.fromCallable(() -> "result"); .subscribe( s -> System.out.println(s), e -> System.out.println("Error"), () -> System.out.println("Completed") );
  12. Observable.interval(1, TimeUnit.SECONDS) .take(2)
 .subscribe( n -> System.out.println(n), e -> System.out.println(“Error"),

    () -> System.out.println("Completed") ); Output: 0 1 Completed Filtering Observable.interval(1, TimeUnit.SECONDS) .take(2)
 .subscribe( n -> System.out.println(n), e -> System.out.println(“Error"), () -> System.out.println("Completed") ); Observable.interval(1, TimeUnit.SECONDS) .take(2)
 .subscribe( n -> System.out.println(n), e -> System.out.println(“Error"), () -> System.out.println("Completed") );
  13. Observable.just("A", "B")
 .flatMap(e -> Observable.just(1, 2).map(n -> e + n))


    .subscribe(
 n -> System.out.println(n),
 e -> System.out.println("Error"),
 () -> System.out.println("Completed")
 ); Output: A1 A2 B1 B2 Completed Transforming Observable.just("A", "B")
 .flatMap(e -> Observable.just(1, 2).map(n -> e + n))
 .subscribe(
 n -> System.out.println(n),
 e -> System.out.println("Error"),
 () -> System.out.println("Completed")
 ); Observable.just("A", "B")
 .flatMap(e -> Observable.just(1, 2).map(n -> e + n))
 .subscribe(
 n -> System.out.println(n),
 e -> System.out.println("Error"),
 () -> System.out.println("Completed")
 );
  14. Observable.zip(
 Observable.just("A", "B", "C"),
 Observable.interval(1, TimeUnit.SECONDS),
 (a, n) -> a

    + n
 ).subscribe(
 n -> System.out.println(n),
 e -> System.out.println("Error"),
 () -> System.out.println("Completed")
 ); Output: A0 B1 C2 Completed Combining Observable.zip(
 Observable.just("A", "B", "C"),
 Observable.interval(1, TimeUnit.SECONDS),
 (a, n) -> a + n
 ).subscribe(
 n -> System.out.println(n),
 e -> System.out.println("Error"),
 () -> System.out.println("Completed")
 );
  15. Multithreading Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .filter(n

    -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd(“Subscriber")); printThreadNameAnd("Done subscribing"); Observable Operator 1 Operator 2 Subscriber Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd(“Subscriber")); printThreadNameAnd("Done subscribing");
  16. Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .filter(n ->

    {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd(“Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd(“Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd(“Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd(“Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd(“Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd(“Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd(“Subscriber")); printThreadNameAnd("Done subscribing"); Output: main: Observable main: Operator 1 main: Operator 2 main: Subscriber main: Done subscribing RxJava is blocking by default Multithreading
  17. Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n

    -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Output: main: Done subscribing RxCachedThreadScheduler-1: Observable RxCachedThreadScheduler-1: Operator 1 RxCachedThreadScheduler-1: Operator 2 RxCachedThreadScheduler-1: Subscriber Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 })
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Multithreading
  18. Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n

    -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 }) .observeOn(Schedulers.newThread())
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Output: main: Done subscribing RxCachedThreadScheduler-1: Observable RxCachedThreadScheduler-1: Operator 1 RxNewThreadScheduler-1: Operator 2 RxNewThreadScheduler-1: Subscriber Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 }) .observeOn(Schedulers.newThread())
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 }) .observeOn(Schedulers.newThread())
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 }) .observeOn(Schedulers.newThread())
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 }) .observeOn(Schedulers.newThread())
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 }) .observeOn(Schedulers.newThread())
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 }) .observeOn(Schedulers.newThread())
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Observable
 .fromCallable(() -> {
 printThreadNameAnd("Observable");
 return 1;
 })
 .subscribeOn(Schedulers.io())
 .filter(n -> {
 printThreadNameAnd("Operator 1");
 return n == 1;
 }) .observeOn(Schedulers.newThread())
 .map(n -> {
 printThreadNameAnd("Operator 2");
 return "A" + n;
 })
 .subscribe(n -> printThreadNameAnd("Subscriber")); printThreadNameAnd("Done subscribing"); Multithreading
  19. Observable.interval(1, TimeUnit.SECONDS)
 .take(3)
 .subscribe(
 n -> printThreadNameAnd("Subscriber"),
 e -> System.out.println("Error"),


    () -> System.out.println("Completed")
 ); Output: RxComputationThreadPool-1: Subscriber RxComputationThreadPool-1: Subscriber RxComputationThreadPool-1: Subscriber Completed Scheduler Scheduler: interval operates by default on the computation Scheduler.
  20. Default error handling Observable.fromCallable(() -> { throw new IOException(); })


    .subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Output: Error: IOException Observable.fromCallable(() -> { throw new IOException(); })
 .subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Observable.fromCallable(() -> { throw new IOException(); })
 .subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Observable throws Exception handled in onError
  21. Default error handling Observable.just("A")
 .map(text -> Integer.parseInt(text))
 .subscribe(
 n ->

    System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Output: Error: NumberFormatException Observable.just("A")
 .map(text -> Integer.parseInt(text))
 .subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Observable.just("A")
 .map(text -> Integer.parseInt(text))
 .subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Operator throws Exception handled in onError
  22. Errors not handled Observable.just("A")
 .map(text -> Integer.parseInt(text))
 .subscribe(
 n ->

    System.out.println(n),
 throwable -> { throw new RuntimeException(); },
 () -> System.out.println("Completed")
 ); Output: Exception in thread "main" rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError Observable.just("A")
 .map(text -> Integer.parseInt(text))
 .subscribe(
 n -> System.out.println(n),
 throwable -> { throw new RuntimeException(); },
 () -> System.out.println("Completed")
 ); Observable.just("A")
 .map(text -> Integer.parseInt(text))
 .subscribe(
 n -> System.out.println(n),
 throwable -> { throw new RuntimeException(); },
 () -> System.out.println("Completed")
 ); onError throws Exception
  23. Errors not handled Observable.just("A")
 .map(text -> Integer.parseInt(text))
 .subscribe(
 n ->

    System.out.println(n)
 ); Output: Exception in thread "main" rx.exceptions.OnErrorNotImplementedException Observable.just("A")
 .map(text -> Integer.parseInt(text))
 .subscribe(
 n -> System.out.println(n)
 ); onError function missing
  24. Closing the stream gracefully Observable.error(new IOException())
 .onErrorReturn(throwable -> -1)
 .subscribe(


    n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Output: -1 Completed Observable.error(new IOException())
 .onErrorReturn(throwable -> -1)
 .subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Observable.error(new IOException())
 .onErrorReturn(throwable -> -1)
 .subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); handling error by retuning a value indicating error
  25. Using a backup Observable<Integer> aService = Observable.error(new IOException());
 Observable<Integer> backupService

    = Observable.just(1); 
 aService
 .onErrorResumeNext(backupService)
 .subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Output: 1 Completed Observable<Integer> aService = Observable.error(new IOException());
 Observable<Integer> backupService = Observable.just(1); 
 aService
 .onErrorResumeNext(backupService)
 .subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Observable<Integer> aService = Observable.error(new IOException());
 Observable<Integer> backupService = Observable.just(1); 
 aService
 .onErrorResumeNext(backupService)
 .subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); resuming with a backup service
  26. Retrying Observable.error(new IOException())
 .retryWhen(errorStream ->
 Observable.range(1, 3)
 .zipWith(errorStream, (n, e)

    -> n)
 .doOnNext(n -> System.out.println("Retry: " + n))
 ).subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Output: Retry: 1 Retry: 2 Retry: 3 Completed Observable.error(new IOException())
 .retryWhen(errorStream ->
 Observable.range(1, 3)
 .zipWith(errorStream, (n, e) -> n)
 .doOnNext(n -> System.out.println("Retry: " + n))
 ).subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); Observable.error(new IOException())
 .retryWhen(errorStream ->
 Observable.range(1, 3)
 .zipWith(errorStream, (n, e) -> n)
 .doOnNext(n -> System.out.println("Retry: " + n))
 ).subscribe(
 n -> System.out.println(n),
 throwable -> printErrorClassName(throwable),
 () -> System.out.println("Completed")
 ); conditional retry logic
  27. How to think in RxJava • Introduction • Operators •

    Multithreading • Resiliency • Testing Utilities
  28. TestSubscriber TestSubscriber<Long> testSubscriber = TestSubscriber.create(); Observable.interval(1, TimeUnit.SECONDS).take(2)
 .subscribe(testSubscriber); 
 testSubscriber.awaitTerminalEvent();


    testSubscriber.assertCompleted(); testSubscriber.assertNoErrors(); testSubscriber.assertUnsubscribed(); testSubscriber.assertValueCount(2);
 testSubscriber.assertValues(0L, 1L);
  29. TestScheduler TestScheduler testScheduler = Schedulers.test(); TestSubscriber<Long> testSubscriber = TestSubscriber.create(); 


    Observable.interval(1, TimeUnit.SECONDS, testScheduler).take(2)
 .subscribe(testSubscriber); 
 testScheduler.advanceTimeTo(1500, TimeUnit.MILLISECONDS);
 testSubscriber.assertValueCount(1);
 testSubscriber.assertNotCompleted(); 
 testScheduler.advanceTimeBy(1000, TimeUnit.MILLISECONDS);
 testSubscriber.assertValueCount(2);
 testSubscriber.assertCompleted();
  30. How to think in RxJava • Introduction • Operators •

    Multithreading • Resiliency • Testing Utilities
  31. fromCallable timer interval just error Observable Subscriber onError onNext onCompleted

    Observer Pattern Reactive map flatMap timestamp filter distinctUntilChanged take buffer zip combineLatest concat merge Functional observeOn subscribeOn Scheduler io computation newThread Asynchronous TestSubscriber TestScheduler Resiliency retry timeout onErrorResumeNext Observable Subscriber onError onNext onCompleted Observer Pattern Reactive Concept fromCallable timer interval just error Create map flatMap timestamp filter distinctUntilChanged take buffer zip combineLatest concat merge Functional Operate observeOn subscribeOn Scheduler io computation newThread Asynchronous Resiliency retry timeout onErrorResumeNext TestSubscriber TestScheduler Concurrent Handle errors Test How to think in RxJava