Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

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. Operator Reuse • Problem: Duplicated code Observable.just("...Some long running operation...")


    .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(System.out::println);
  2. 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());
 }
  3. 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!")
 );
  4. 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!")
 );
  5. 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;
 }
 }
  6. Custom Operators • Avoid custom Operators if possible • Obey

    the Observable contract • Plenty of samples in core library
  7. Subscriptions • Common problem: Memory leaks! • Two conditions: •

    Sequences with references • Delayed (or never) ending Observables
  8. Default Schedulers • Operators can have default schedulers • Example:

    Observable.interval() • Read the documentation!
  9. Hot vs. Cold • Hot: Sequences producing notifications regardless of

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

    effects. • Cold: Sequences may have subscription side effects.
  11. 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()
  12. Temperature Conversion • Hot -> Cold: defer() • Hot ->

    Cold (incidental): merge(), concat(), etc. • Cold -> Hot: publish()
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. Subject • Implements Observer and Observable • Subjects are hot

    • Easy way to emit items manually to a sequence
  20. 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();
 }
 }
  21. Why NOT Subjects? • Mutable state in functional world •

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

    Use operators instead • AsyncSubject -> takeLast(1) • PublishSubject -> publish() • ReplaySubject -> replay()/cache() • BehaviorSubject -> replay(1)/cache(1)
  23. 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...
 });
  24. Operator vs. Pull • Pull - cold Observables • Pull

    - supported operators • Produce less for everything else
  25. 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