Save 37% off PRO during our Black Friday Sale! »

Reactive Extensions: Beyond the Basics

Reactive Extensions: Beyond the Basics

A (possibly) helpful talk after you've learned the basic reactive extensions pattern. Given at MinneBar 2015.

It has a basis in RxJava, but many of the concepts apply generally to any reactive extension framework.

Recording here: https://www.youtube.com/watch?v=Jt-_oVQVZlQ

D225ebf0faa666ac7655cc7e4689283c?s=128

Daniel Lew
PRO

April 11, 2015
Tweet

Transcript

  1. Reactive Extensions:
 Beyond the Basics Dan Lew April 11, 2015

  2. Composition using compose()

  3. Operator Reuse • Problem: Duplicated code Observable.just("...Some long running operation...")


    .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(System.out::println);
  4. compose() • compose() Observable.just("...Some long running operation...")
 .compose(applySchedulers())
 .subscribe(System.out::println); •

    Transformer <T> Observable.Transformer<T, T> applySchedulers() {
 return observable -> observable.subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread());
 }
  5. Lift() me up Custom Operators

  6. Custom Operators • Reactive extensions don’t cover everything

  7. Contrived Example Observable.interval(100, TimeUnit.MILLISECONDS)
 .takeUntil(Observable.timer(1, TimeUnit.SECONDS))
 .subscribe(
 i -> System.out.println("onNext!"),


    e -> System.out.println("onError!"),
 () -> System.out.println("onComplete!")
 );
  8. Contrived Example Observable.interval(100, TimeUnit.MILLISECONDS)
 .takeUntil(Observable.timer(1, TimeUnit.SECONDS))
 .subscribe(
 i -> System.out.println("onNext!"),


    e -> System.out.println("onError!"),
 () -> System.out.println("onComplete!")
 );
  9. Contrived Example Observable.interval(100, TimeUnit.MILLISECONDS)
 .takeUntil(Observable.timer(1, TimeUnit.SECONDS))
 .subscribe(
 i -> System.out.println("onNext!"),


    e -> System.out.println("onError!"),
 () -> System.out.println("onComplete!")
 );
  10. Contrived Example Observable.interval(100, TimeUnit.MILLISECONDS)
 .lift(new OperatorSubscribeUntil<>(Observable.timer(1, TimeUnit.SECONDS)))
 .subscribe(
 i ->

    System.out.println("onNext!"),
 e -> System.out.println("onError!"),
 () -> System.out.println("onComplete!")
 );
  11. Contrived Example Observable.interval(100, TimeUnit.MILLISECONDS)
 .lift(new OperatorSubscribeUntil<>(Observable.timer(1, TimeUnit.SECONDS)))
 .subscribe(
 i ->

    System.out.println("onNext!"),
 e -> System.out.println("onError!"),
 () -> System.out.println("onComplete!")
 );
  12. final class OperatorSubscribeUntil<T, R> implements Observable.Operator<T, T> {
 private final

    Observable<? extends R> other;
 
 public OperatorSubscribeUntil(final Observable<? extends R> other) {
 this.other = other;
 }
 
 @Override
 public Subscriber<? super T> call(final Subscriber<? super T> child) {
 final Subscriber<T> parent = new SerializedSubscriber<T>(child);
 
 other.unsafeSubscribe(new Subscriber<R>(child) {
 @Override
 public void onCompleted() {
 parent.unsubscribe();
 }
 
 @Override
 public void onError(Throwable e) {
 parent.onError(e);
 }
 
 @Override
 public void onNext(R t) {
 parent.unsubscribe();
 }
 });
 
 return parent;
 }
 }
  13. Custom Operators • Avoid custom Operators if possible • Obey

    the Observable contract • Plenty of samples in core library
  14. Subscriptions Avoid being a hoarder

  15. Subscriptions • Common problem: Memory leaks! • Two conditions: •

    Sequences with references • Delayed (or never) ending Observables
  16. Finite, With Reference Observable.just(1).subscribe(this::doSomething);

  17. Never-ending, No Reference Observable.never().subscribe();

  18. Never-ending, With Reference Observable.interval(1, TimeUnit.SECONDS)
 .subscribe(this::doSomething);

  19. Solution • Unsubscribe! Subscription subscription = Observable.just(1).subscribe();
 subscription.unsubscribe(); • General:

    CompositeSubscription • Android: LifecycleObservable
  20. Scheduling Oddities Understanding Schedulers

  21. Mysteries Observable.just(1, 2, 3)
 .subscribeOn(Schedulers.newThread())
 .subscribeOn(Schedulers.io())
 .subscribe(System.out::println); • How many

    threads are spawned? • What thread is just() on? • What thread is subscribe() on?
  22. Default Schedulers • Operators can have default schedulers • Example:

    Observable.interval() • Read the documentation!
  23. Hot n Cold …Observables

  24. Hot vs. Cold • Hot: Sequences producing notifications regardless of

    subscriptions • Cold: Sequences that produce notifications on subscription
  25. Hot vs. Cold • Hot: Sequences have no subscription side

    effects. • Cold: Sequences may have subscription side effects.
  26. Hot or Not? • Observable.just(“1”) • PublishSubject • ViewObservable.clicks() •

    Observable.interval() • ReplaySubject
  27. Why should I care? • Operations can be expensive •

    Sometimes you don’t want side effects • Example: Network calls • Sometimes you do want side effects • Example: retry()
  28. Temperature Conversion • Hot -> Cold: defer() • Hot ->

    Cold (incidental): merge(), concat(), etc. • Cold -> Hot: publish()
  29. Determining Temperature • You cannot do it • Assume cold

  30. Learning to Share …Observables

  31. Why Share? • Cold observables generate notifications • Generating notifications

    can take time
  32. Example Observable<Long> observable = Observable.create(subscriber -> {
 subscriber.onNext(Calendar.getInstance().getTimeInMillis());
 subscriber.onCompleted();
 });


    
 observable.subscribe(l -> System.out.println("s1: " + l));
 observable.subscribe(l -> System.out.println("s2: " + l)); // Outputs… s1: 1428676059925 s2: 1428676059931
  33. Publish • publish() creates a ConnectableObservable • Generates on connect(),

    not subscribe()
  34. publish() Observable<Long> observable = Observable.create(subscriber -> {
 subscriber.onNext(Calendar.getInstance().getTimeInMillis());
 subscriber.onCompleted();
 });


    
 ConnectableObservable<Long> connectable = observable.publish();
 connectable.subscribe(l -> System.out.println("s1: " + l));
 connectable.subscribe(l -> System.out.println("s2: " + l));
 connectable.connect(); // Outputs… s1: 1428676170160 s2: 1428676170160
  35. publish() Observable<Long> observable = Observable.create(subscriber -> {
 subscriber.onNext(Calendar.getInstance().getTimeInMillis());
 subscriber.onCompleted();
 });


    
 ConnectableObservable<Long> connectable = observable.publish();
 connectable.subscribe(l -> System.out.println("s1: " + l));
 connectable.subscribe(l -> System.out.println("s2: " + l));
 connectable.connect(); // Outputs… s1: 1428676170160 s2: 1428676170160
  36. refCount() Observable<Long> observable = Observable.<Long>create(subscriber -> {
 subscriber.onNext(Calendar.getInstance().getTimeInMillis());
 subscriber.onCompleted();
 }).delay(100,

    TimeUnit.MILLISECONDS);
 
 Observable<Long> connectable = observable.publish().refCount();
 connectable.subscribe(l -> System.out.println("s1: " + l));
 connectable.subscribe(l -> System.out.println("s2: " + l)); // Outputs… s1: 1428676170160 s2: 1428676170160
  37. share() Observable<Long> observable = Observable.<Long>create(subscriber -> {
 subscriber.onNext(Calendar.getInstance().getTimeInMillis());
 subscriber.onCompleted();
 }).delay(100,

    TimeUnit.MILLISECONDS);
 
 Observable<Long> connectable = observable.share();
 connectable.subscribe(l -> System.out.println("s1: " + l));
 connectable.subscribe(l -> System.out.println("s2: " + l)); // Outputs… s1: 1428676170160 s2: 1428676170160
  38. Pop Quiz Observable<Long> observable = Observable.create(subscriber -> {
 subscriber.onNext(Calendar.getInstance().getTimeInMillis());
 subscriber.onCompleted();


    });
 
 Observable<Long> connectable = observable.share();
 connectable.subscribe(l -> System.out.println("s1: " + l));
 connectable.subscribe(l -> System.out.println("s2: " + l)); // Outputs… s1: 1428676059925 s2: 1428676059931
  39. Don’t mention the war! Avoiding Subjects

  40. Subject • Implements Observer and Observable • Subjects are hot

    • Easy way to emit items manually to a sequence
  41. Track Values • Emit types set in a class public

    class MyClass {
 
 private String value;
 
 private final BehaviorSubject<String> subject =
 BehaviorSubject.create();
 
 public void setValue(String value) {
 this.value = value;
 subject.onNext(value);
 }
 
 public Observable<String> getValueObservable() {
 return subject.asObservable();
 }
 }
  42. Why NOT Subjects? • Mutable state in functional world •

    Difficult to mix mutation + concurrency • There often exists a simpler solution
  43. Avoiding Subjects • If possible, convert data into Observable •

    Use operators instead • AsyncSubject -> takeLast(1) • PublishSubject -> publish() • ReplaySubject -> replay()/cache() • BehaviorSubject -> replay(1)/cache(1)
  44. Under Pressure …Backpressure, that is

  45. Backpressure • Producers gonna produce • Consumers gonna consume •

    Haters gonna hate
  46. What if… • Production < consumption? • Production == consumption?

    • Production > consumption?
  47. Produce Less • filter(), distinctUntilChanged() • throttleLast(), throttleFirst(), debounce() •

    buffer() • onBackPressureDrop()
  48. Reactive Pull • Produce on request observable.subscribe(new Subscriber<Integer>() {
 @Override


    public void onStart() {
 // Start pulling...
 request(1);
 }
 
 @Override
 public void onNext(Integer n) {
 // Use n, then pull again...
 request(1);
 }
 
 // ...Clipped out onError/onCompleted...
 });
  49. Operator vs. Pull • Pull - cold Observables • Pull

    - supported operators • Produce less for everything else
  50. More Reading • Composition: http://blog.danlew.net/2015/03/02/dont-break-the- chain/ • Subscriptions: https://github.com/dlew/android-subscription- leaks

    • Schedulers: http://www.grahamlea.com/2014/07/rxjava- threading-examples/ • Hot vs. Cold: http://davesexton.com/blog/post/Hot-and-Cold- Observables.aspx • Subjects: http://davesexton.com/blog/post/To-Use-Subject-Or- Not-To-Use-Subject.aspx
  51. Thanks! • danlew.net • @danlew42