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

Common RxJava Mistakes

Common RxJava Mistakes

Talk given at Droidcon SF 2016.

Recording here: https://www.youtube.com/watch?v=QdmkXL7XikQ

Daniel Lew

March 17, 2016
Tweet

More Decks by Daniel Lew

Other Decks in Programming

Transcript

  1. Lambda Notation new Func1<String, Integer>() {
 @Override public Integer call(String

    s) {
 return s.length();
 }
 }; s -> {
 return s.length();
 } s -> s.length()
  2. Lambda Notation new Func1<String, Integer>() {
 @Override public Integer call(String

    s) {
 return s.length();
 }
 }; (String s) -> {
 return s.length();
 } s -> s.length() …is the same as…
  3. Lambda Notation new Func1<String, Integer>() {
 @Override public Integer call(String

    s) {
 return s.length();
 }
 }; (String s) -> {
 return s.length();
 } s -> s.length() …is the same as… …is the same as…
  4. Observable<String> obs = Observable.just("Hello!");
 obs.map(s -> s.length());
 obs.subscribe(i -> System.out.println(i));

    Observable.just("Hello!")
 .map(s -> s.length())
 .subscribe(i -> System.out.println(i)); Versus DOES NOT WORK
  5. class MyClass {
 String value;
 
 Observable<String> value() {
 return

    Observable.create(subscriber -> {
 subscriber.onNext(value);
 subscriber.onCompleted();
 });
 }
 } ???
  6. class MyClass {
 String value;
 
 Observable<String> value() {
 return

    Observable.create(subscriber -> {
 subscriber.onNext(value);
 subscriber.onCompleted();
 });
 }
 } BAD
  7. class MyClass {
 String value;
 
 Observable<String> value() {
 return

    Observable.create(subscriber -> {
 subscriber.onNext(value);
 subscriber.onCompleted();
 });
 }
 } return Observable.fromCallable(() -> value);
  8. class MyClass {
 String value;
 
 Observable<String> value() {
 return

    Observable.create(subscriber -> {
 subscriber.onNext(value);
 subscriber.onCompleted();
 });
 }
 } return Observable.defer(() -> Observable.just(value));
  9. class MyClass {
 BehaviorSubject<String> value;
 
 Observable<String> value() {
 return

    value.asObservable();
 }
 
 void setValue(String s) {
 value.onNext(s);
 }
 }
  10. Lessons • Learn your creation methods! • Resort to create()

    when appropriate • (which is almost never)
  11. Exercise • Find an API with search • Query search

    results via Observable • Request extra data on results via Observable • Use only one subscribe()
  12. flatMap vs. concatMap • Same, except… • #ordermatters • flatMap

    -> Unordered merge • concatMap -> Ordered merge
  13. List of Lists Observable<List<String>> obs = /* ...etc... */;
 


    obs.flatMap(list -> Observable.from(list))
 .map(/* ...something... */)
 .toList();
 
 obs.flatMap(
 list -> Observable.from(list)
 .map(/* ...something... */)
 .toList()
 );
  14. List of Lists Observable<List<String>> obs = /* ...etc... */;
 


    obs.flatMap(list -> Observable.from(list))
 .map(/* ...something... */)
 .toList();
 
 obs.flatMap(
 list -> Observable.from(list)
 .map(/* ...something... */)
 .toList()
 ); result: 1 list
  15. List of Lists Observable<List<String>> obs = /* ...etc... */;
 


    obs.flatMap(list -> Observable.from(list))
 .map(/* ...something... */)
 .toList();
 
 obs.flatMap(
 list -> Observable.from(list)
 .map(/* ...something... */)
 .toList()
 ); result: 1 list result: N lists
  16. take(1) vs. first() • take(1) - emits once, or not

    at all Observable.empty().take(1) • first() - emits once, or crashes Observable.empty().first() crash! emits nothing
  17. cache() vs replay() • Don’t use cache() • Use replay()

    • cache() == replay().autoConnect()
  18. Why not? • Each operator —> different flow • Framework

    handles common flows • Resort to custom operators for unique flows • Operators are hard!
  19. Observer<Integer> observer = /* some Observer */
 o1.subscribe(observer);
 o2.subscribe(observer); Subscriber<Integer>

    subscriber = /* some Subscriber */ o1.subscribe(subscriber);
 o2.subscribe(subscriber); Versus
  20. • Observer is an interface • Subscriber is a class

    • Observer is stateless • Subscriber is stateful
  21. Observer<Integer> observer = /* some Observer */
 o1.subscribe(observer);
 o2.subscribe(observer); Subscriber<Integer>

    subscriber = /* some Subscriber */ o1.subscribe(subscriber);
 o2.subscribe(subscriber); Versus Can blow up in your face
  22. Debugging onError Exception in thread "RxCachedThreadScheduler-1" java.lang.IllegalStateException: Exception thrown on

    Scheduler.Worker thread. Add `onError` handling.
 at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:60)
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
 at java.util.concurrent.FutureTask.run(FutureTask.java:266)
 at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
 at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 at java.lang.Thread.run(Thread.java:745)
 Caused by: rx.exceptions.OnErrorNotImplementedException: Sequence contains no elements
 at rx.Observable$26.onError(Observable.java:7881)
 at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:159)
 at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:120)
 at rx.internal.operators.OperatorSubscribeOn$1$1$1.onError(OperatorSubscribeOn.java:71)
 at rx.internal.operators.OperatorSingle$ParentSubscriber.onCompleted(OperatorSingle.java:131)
 at rx.internal.operators.OperatorTake$1.onCompleted(OperatorTake.java:53)
 at rx.Observable$EmptyHolder$1.call(Observable.java:1073)
 at rx.Observable$EmptyHolder$1.call(Observable.java:1070)
 at rx.Observable$2.call(Observable.java:162)
 at rx.Observable$2.call(Observable.java:154)
 at rx.Observable$2.call(Observable.java:162)
 at rx.Observable$2.call(Observable.java:154)
 at rx.Observable.unsafeSubscribe(Observable.java:8098)
 at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62)
 at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
 ... 7 more
 Caused by: java.util.NoSuchElementException: Sequence contains no elements
 ... 18 more No references to my code!
  23. Implement onError Observable.empty()
 .first()
 .subscribeOn(Schedulers.io())
 .subscribe(o -> System.out.println(o),
 err ->

    {
 throw new OnErrorNotImplementedException("Source!", err);
 }); Caused by: rx.exceptions.OnErrorNotImplementedException: Source!
 at net.danlew.experiments.Tester.lambda$main$1(Tester.java:52)
  24. Implement onError Observable.empty()
 .first()
 .subscribeOn(Schedulers.io())
 .subscribe(o -> System.out.println(o),
 err ->

    {
 throw new OnErrorNotImplementedException("Source!", err);
 }); Caused by: rx.exceptions.OnErrorNotImplementedException: Source!
 at net.danlew.experiments.Tester.lambda$main$1(Tester.java:52)
  25. Implement onError Observable.empty()
 .first()
 .subscribeOn(Schedulers.io())
 .subscribe(o -> System.out.println(o),
 err ->

    {
 throw new OnErrorNotImplementedException("Source!", err);
 }); Caused by: rx.exceptions.OnErrorNotImplementedException: Source!
 at net.danlew.experiments.Tester.lambda$main$1(Tester.java:52)
  26. Sharing • Wanted: Multiple subscribers retrofitRequest.subscribe(/* do a thing */);


    retrofitRequest.subscribe(/* do another thing */); • Bad: Duplicate expensive operations
  27. Sharing ConnectableObservable<Long> obs =
 Observable.fromCallable(() -> System.nanoTime())
 .publish();
 
 obs.subscribe(l

    -> System.out.println("1: " + l));
 obs.subscribe(l -> System.out.println("2: " + l));
 
 obs.connect();
  28. Sharing ConnectableObservable<Long> obs =
 Observable.fromCallable(() -> System.nanoTime())
 .publish();
 
 obs.subscribe(l

    -> System.out.println("1: " + l));
 obs.subscribe(l -> System.out.println("2: " + l));
 
 obs.connect();
  29. Sharing ConnectableObservable<Long> obs =
 Observable.fromCallable(() -> System.nanoTime())
 .publish();
 
 obs.subscribe(l

    -> System.out.println("1: " + l));
 obs.subscribe(l -> System.out.println("2: " + l));
 
 obs.connect();
  30. Sharing ConnectableObservable<Long> obs =
 Observable.fromCallable(() -> System.nanoTime())
 .publish();
 
 obs.subscribe(l

    -> System.out.println("1: " + l));
 obs.subscribe(l -> System.out.println("2: " + l));
 
 obs.connect();
  31. Sharing ConnectableObservable<Long> obs =
 Observable.fromCallable(() -> System.nanoTime())
 .publish();
 
 obs.subscribe(l

    -> System.out.println("1: " + l));
 obs.subscribe(l -> System.out.println("2: " + l));
 
 obs.connect();
  32. refCount Observable<Long> obs =
 Observable.fromCallable(() -> System.nanoTime())
 .publish() .refCount();
 


    obs.subscribe(l -> System.out.println("1: " + l));
 obs.subscribe(l -> System.out.println("2: " + l)); // Output 1: 395093905388878 2: 395093906717841
  33. refCount Observable<Long> obs =
 Observable.fromCallable(() -> System.nanoTime())
 .publish() .refCount();
 


    obs.subscribe(l -> System.out.println("1: " + l));
 obs.subscribe(l -> System.out.println("2: " + l)); // Output 1: 395093905388878 2: 395093906717841
  34. refCount Observable<Long> obs =
 Observable.fromCallable(() -> System.nanoTime())
 .publish() .refCount();
 


    obs.subscribe(l -> System.out.println("1: " + l));
 obs.subscribe(l -> System.out.println("2: " + l)); // Output 1: 395093905388878 2: 395093906717841 huh?