Share
Embed
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
Transcript
51 slides
-
Operator Reuse • Problem: Duplicated code Observable.just("...Some long running operation...")
.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(System.out::println); -
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()); } -
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!") ); -
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!") ); -
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!") ); -
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!") ); -
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!") ); -
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; } } -
Custom Operators • Avoid custom Operators if possible • Obey
the Observable contract • Plenty of samples in core library -
Subscriptions • Common problem: Memory leaks! • Two conditions: •
Sequences with references • Delayed (or never) ending Observables -
Solution • Unsubscribe! Subscription subscription = Observable.just(1).subscribe(); subscription.unsubscribe(); • General:
CompositeSubscription • Android: LifecycleObservable -
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? -
Default Schedulers • Operators can have default schedulers • Example:
Observable.interval() • Read the documentation! -
Hot vs. Cold • Hot: Sequences producing notifications regardless of
subscriptions • Cold: Sequences that produce notifications on subscription -
Hot vs. Cold • Hot: Sequences have no subscription side
effects. • Cold: Sequences may have subscription side effects. -
Hot or Not? • Observable.just(“1”) • PublishSubject • ViewObservable.clicks() •
Observable.interval() • ReplaySubject -
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() -
Temperature Conversion • Hot -> Cold: defer() • Hot ->
Cold (incidental): merge(), concat(), etc. • Cold -> Hot: publish() -
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 -
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 -
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 -
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 -
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 -
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 -
Subject • Implements Observer and Observable • Subjects are hot
• Easy way to emit items manually to a sequence -
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(); } } -
Why NOT Subjects? • Mutable state in functional world •
Difficult to mix mutation + concurrency • There often exists a simpler solution -
Avoiding Subjects • If possible, convert data into Observable •
Use operators instead • AsyncSubject -> takeLast(1) • PublishSubject -> publish() • ReplaySubject -> replay()/cache() • BehaviorSubject -> replay(1)/cache(1) -
Backpressure • Producers gonna produce • Consumers gonna consume •
Haters gonna hate -
What if… • Production < consumption? • Production == consumption?
• Production > consumption? -
Produce Less • filter(), distinctUntilChanged() • throttleLast(), throttleFirst(), debounce() •
buffer() • onBackPressureDrop() -
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... }); -
Operator vs. Pull • Pull - cold Observables • Pull
- supported operators • Produce less for everything else -
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