$30 off During Our Annual Pro Sale. View Details »

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

Daniel Lew

April 11, 2015
Tweet

More Decks by Daniel Lew

Other Decks in Programming

Transcript

  1. Reactive Extensions:

    Beyond the Basics
    Dan Lew
    April 11, 2015

    View Slide

  2. Composition
    using compose()

    View Slide

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

    .subscribeOn(Schedulers.io())

    .observeOn(AndroidSchedulers.mainThread())

    .subscribe(System.out::println);

    View Slide

  4. compose()
    • compose()
    Observable.just("...Some long running operation...")

    .compose(applySchedulers())

    .subscribe(System.out::println);
    • Transformer
    Observable.Transformer applySchedulers() {

    return observable -> observable.subscribeOn(Schedulers.io())

    .observeOn(AndroidSchedulers.mainThread());

    }

    View Slide

  5. Lift() me up
    Custom Operators

    View Slide

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

    View Slide

  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!")

    );

    View Slide

  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!")

    );

    View Slide

  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!")

    );

    View Slide

  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!")

    );

    View Slide

  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!")

    );

    View Slide

  12. final class OperatorSubscribeUntil implements Observable.Operator {

    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 parent = new SerializedSubscriber(child);


    other.unsafeSubscribe(new Subscriber(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;

    }

    }

    View Slide

  13. Custom Operators
    • Avoid custom Operators if possible
    • Obey the Observable contract
    • Plenty of samples in core library

    View Slide

  14. Subscriptions
    Avoid being a hoarder

    View Slide

  15. Subscriptions
    • Common problem: Memory leaks!
    • Two conditions:
    • Sequences with references
    • Delayed (or never) ending Observables

    View Slide

  16. Finite, With Reference
    Observable.just(1).subscribe(this::doSomething);

    View Slide

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

    View Slide

  18. Never-ending, With
    Reference
    Observable.interval(1, TimeUnit.SECONDS)

    .subscribe(this::doSomething);

    View Slide

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

    subscription.unsubscribe();
    • General: CompositeSubscription
    • Android: LifecycleObservable

    View Slide

  20. Scheduling Oddities
    Understanding Schedulers

    View Slide

  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?

    View Slide

  22. Default Schedulers
    • Operators can have default schedulers
    • Example: Observable.interval()
    • Read the documentation!

    View Slide

  23. Hot n Cold
    …Observables

    View Slide

  24. Hot vs. Cold
    • Hot: Sequences producing notifications regardless
    of subscriptions
    • Cold: Sequences that produce notifications on
    subscription

    View Slide

  25. Hot vs. Cold
    • Hot: Sequences have no subscription side effects.
    • Cold: Sequences may have subscription side
    effects.

    View Slide

  26. Hot or Not?
    • Observable.just(“1”)
    • PublishSubject
    • ViewObservable.clicks()
    • Observable.interval()
    • ReplaySubject

    View Slide

  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()

    View Slide

  28. Temperature Conversion
    • Hot -> Cold: defer()
    • Hot -> Cold (incidental): merge(), concat(), etc.
    • Cold -> Hot: publish()

    View Slide

  29. Determining Temperature
    • You cannot do it
    • Assume cold

    View Slide

  30. Learning to Share
    …Observables

    View Slide

  31. Why Share?
    • Cold observables generate notifications
    • Generating notifications can take time

    View Slide

  32. Example
    Observable 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

    View Slide

  33. Publish
    • publish() creates a ConnectableObservable
    • Generates on connect(), not subscribe()

    View Slide

  34. publish()
    Observable observable = Observable.create(subscriber -> {

    subscriber.onNext(Calendar.getInstance().getTimeInMillis());

    subscriber.onCompleted();

    });


    ConnectableObservable 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

    View Slide

  35. publish()
    Observable observable = Observable.create(subscriber -> {

    subscriber.onNext(Calendar.getInstance().getTimeInMillis());

    subscriber.onCompleted();

    });


    ConnectableObservable 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

    View Slide

  36. refCount()
    Observable observable = Observable.create(subscriber -> {

    subscriber.onNext(Calendar.getInstance().getTimeInMillis());

    subscriber.onCompleted();

    }).delay(100, TimeUnit.MILLISECONDS);


    Observable 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

    View Slide

  37. share()
    Observable observable = Observable.create(subscriber -> {

    subscriber.onNext(Calendar.getInstance().getTimeInMillis());

    subscriber.onCompleted();

    }).delay(100, TimeUnit.MILLISECONDS);


    Observable connectable = observable.share();

    connectable.subscribe(l -> System.out.println("s1: " + l));

    connectable.subscribe(l -> System.out.println("s2: " + l));
    // Outputs…
    s1: 1428676170160
    s2: 1428676170160

    View Slide

  38. Pop Quiz
    Observable observable = Observable.create(subscriber -> {

    subscriber.onNext(Calendar.getInstance().getTimeInMillis());

    subscriber.onCompleted();

    });


    Observable connectable = observable.share();

    connectable.subscribe(l -> System.out.println("s1: " + l));

    connectable.subscribe(l -> System.out.println("s2: " + l));
    // Outputs…
    s1: 1428676059925
    s2: 1428676059931

    View Slide

  39. Don’t mention the war!
    Avoiding Subjects

    View Slide

  40. Subject
    • Implements Observer and Observable
    • Subjects are hot
    • Easy way to emit items manually to a sequence

    View Slide

  41. Track Values
    • Emit types set in a class
    public class MyClass {


    private String value;


    private final BehaviorSubject subject =

    BehaviorSubject.create();


    public void setValue(String value) {

    this.value = value;

    subject.onNext(value);

    }


    public Observable getValueObservable() {

    return subject.asObservable();

    }

    }

    View Slide

  42. Why NOT Subjects?
    • Mutable state in functional world
    • Difficult to mix mutation + concurrency
    • There often exists a simpler solution

    View Slide

  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)

    View Slide

  44. Under Pressure
    …Backpressure, that is

    View Slide

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

    View Slide

  46. What if…
    • Production < consumption?
    • Production == consumption?
    • Production > consumption?

    View Slide

  47. Produce Less
    • filter(), distinctUntilChanged()
    • throttleLast(), throttleFirst(), debounce()
    • buffer()
    • onBackPressureDrop()

    View Slide

  48. Reactive Pull
    • Produce on request
    observable.subscribe(new Subscriber() {

    @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...

    });

    View Slide

  49. Operator vs. Pull
    • Pull - cold Observables
    • Pull - supported operators
    • Produce less for everything else

    View Slide

  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

    View Slide

  51. Thanks!
    • danlew.net
    • @danlew42

    View Slide