Share
Embed
Transcript
45 slides
-
new ClickObservable(footerButton) .observeOn(AndroidSchedulers.mainThread()) .flatMap(!__ !-> getNetworkPassword(adapter.list.get(adapter.selected))) .flatMap(networkInfoPair !-> { return
interceptWifiWatchdogs() .andThen(Completable.defer(this!::handlePreviousSetupAttempt)) .observeOn(AndroidSchedulers.mainThread()) .andThen(Single.defer(this!::handleMarshmallowPermissions)) .map(!__ !-> networkInfoPair); }) .flatMap(pair !-> provisionWifiDevice(pair.first, pair.second)) .flatMap(!__ !-> awaitDeviceOnline(lock, targetDevice) .flatMap(isOnline !-> { if (isOnline) return Single.just(isOnline); else return onWifiConfigError(new VenusSetupError(VenusError.BRIDGE_OFFLINE)) })) .observeOn(AndroidSchedulers.mainThread()) .onErrorResumeNext(this!::onWifiConfigError) .subscribe(o !-> completionSignal.onComplete(), throwable !-> completionSignal!::onError); Extending RxJava Writing Your First Custom Operator 1 bit.ly/2lBsBge -
Kotlin? 4 +public class ObservableFilterMap<In, Out> extends Observable<Out> { private
final Function<In, Out> mapper; private final Predicate<In> filter; public ObservableFilterMap(Predicate<In> filter, Function<In, Out> mapper) { this.filter = filter; this.mapper = mapper; } @Override protected void subscribeActual(Observer<? super Out> observer) { new MapFilterObserver!<>(observer, filter, mapper); } !!... } == -
Table of Contents I. What is RxJava? II. Anatomy of
a Custom Operator ‣ ClickObservable ‣ MapFilterObservable III. Observable!::create IV.Questions 5 -
RxJava is... • asynchronous (sometimes!) • event-based stream • composable
• backpressure-sensitive 7 -
Backpressure. • rapid/large number of emissions • bursty • multiple
mismatched sources 9 • slow to consume (consumption is expensive) • blocking I/O • network • disk • carrier pigeon Producer (upstream) Consumer (downstream) -
Q: Do I really care about backpressure? A: Probably not?
(I don't know your life) -
Anatomy of a Custom Operator 1. construct 2. subscribeActual(Observer<?> observer)
3. inner listener... a. implements non-Reactive class ("bridge") b. implements Observer<?> 14 -
Anatomy of a Custom Operator 1. construct 2. subscribeActual(Observer<?> observer)
3. inner listener... a. implements non-Reactive class ("bridge") b. implements Observer<?> 15 -
Custom Operator? 16 new ClickObservable(footerButton) .observeOn(AndroidSchedulers.mainThread()) .flatMap(!__ !-> getNetworkPassword(adapter.list.get(adapter.selected))) .flatMap(networkInfoPair
!-> { return interceptWifiWatchdogs() .andThen(Completable.defer(this!::handlePreviousSetupAttempt)) .observeOn(AndroidSchedulers.mainThread()) .andThen(Single.defer(this!::handleMarshmallowPermissions)) .map(!__ !-> networkInfoPair); }) .flatMap(pair !-> provisionWifiDevice(pair.first, pair.second)) .flatMap(!__ !-> awaitDeviceOnline(lock, targetDevice) .flatMap(isOnline !-> { if (isOnline) return Single.just(isOnline); else return onWifiConfigError(new VenusSetupError(VenusError.BRIDGE_OFFLINE)) })) .observeOn(AndroidSchedulers.mainThread()) .onErrorResumeNext(this!::onWifiConfigError) .subscribe(o !-> completionSignal.onComplete(), throwable !-> completionSignal!::onError); -
Custom Operator! 17 new ClickObservable(footerButton) .observeOn(AndroidSchedulers.mainThread()) .flatMap(!__ !-> getNetworkPassword(adapter.list.get(adapter.selected))) .flatMap(networkInfoPair
!-> { return interceptWifiWatchdogs() .andThen(Completable.defer(this!::handlePreviousSetupAttempt)) .observeOn(AndroidSchedulers.mainThread()) .andThen(Single.defer(this!::handleMarshmallowPermissions)) .map(!__ !-> networkInfoPair); }) .flatMap(pair !-> provisionWifiDevice(pair.first, pair.second)) .flatMap(!__ !-> awaitDeviceOnline(lock, targetDevice) .flatMap(isOnline !-> { if (isOnline) return Single.just(isOnline); else return onWifiConfigError(new VenusSetupError(VenusError.BRIDGE_OFFLINE)) })) .observeOn(AndroidSchedulers.mainThread()) .onErrorResumeNext(this!::onWifiConfigError) .subscribe(o !-> completionSignal.onComplete(), throwable !-> completionSignal!::onError); -
Custom Operator - Constructor 18 final class ClickObservable extends Observable<Boolean>
{ ClickObservable(View view) { !!... } @Override protected void subscribeActual(Observer<? super Boolean> observer) { !!... } } Source(ish): JakeWharton/RxBinding -
Custom Operator - Constructor 19 final class ClickObservable extends Observable<Boolean>
{ private final View view; ClickObservable(View view) { this.view = view; } @Override protected void subscribeActual(Observer<? super Boolean> observer) { !!... } !!... Source(ish): JakeWharton/RxBinding -
Custom Operator: Upstream Subscription 20 final class ClickObservable extends Observable<Boolean>
{ private final View view; ClickObservable(View view) { this.view = view; } @Override protected void subscribeActual(Observer<? super Boolean> observer) { Listener listener = new Listener(view, observer); observer.onSubscribe(listener); view.setOnClickListener(listener); } !!... Source(ish): JakeWharton/RxBinding -
Custom Operator: Upstream Subscription 21 final class ClickObservable extends Observable<Boolean>
{ private final View view; ClickObservable(View view) { this.view = view; } @Override protected void subscribeActual(Observer<? super Boolean> observer) { Listener listener = new Listener(view, observer); observer.onSubscribe(listener); view.setOnClickListener(listener); } !!... Source(ish): JakeWharton/RxBinding void onSubscribe(@NonNull Disposable d); -
Custom Operator: Inner "Bridge" 22 static final class Listener extends
MainThreadDisposable implements OnClickListener { public Listener(View view, Observer<? super Boolean> observer) { !!... } @Override public void onClick(View v) { !!... } @Override protected void onDispose() { !!... } } Source(ish): JakeWharton/RxBinding -
Custom Operator: Inner "Bridge" 23 static final class Listener extends
MainThreadDisposable implements OnClickListener { private final View view; private final Observer<? super Boolean> observer; public Listener(View view, Observer<? super Boolean> observer) { this.view = view; this.observer = observer; } @Override public void onClick(View v) { if (!isDisposed()) { observer.onNext(Boolean.TRUE); } } } Source(ish): JakeWharton/RxBinding -
Custom Operator: Inner "Bridge" 24 static final class Listener extends
MainThreadDisposable implements OnClickListener { private final View view; private final Observer<? super Boolean> observer; public Listener(View view, Observer<? super Boolean> observer) { this.view = view; this.observer = observer; } @Override protected void onDispose() { view.setOnClickListener(null); } @Override public void onClick(View v) { if (!isDisposed()) { observer.onNext(Boolean.TRUE); } } } Source(ish): JakeWharton/RxBinding -
SeekBarObservable!::Listener 25 private static class Listener extends MainThreadDisposable implements OnSeekBarChangeListener
{ private final SeekBar seekBar; private final Observer<? super SeekBarRxEvent> observer; public Listener(SeekBar seekBar, Observer<? super SeekBarRxEvent> observer) { this.seekBar = seekBar; this.observer = observer; } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (!fromUser) return; observer.onNext(new SeekBarRxEvent(EventType.PROGRESS_CHANGED, progress)); } @Override public void onStopTrackingTouch(SeekBar seekBar) { observer.onNext(new SeekBarRxEvent(EventType.STOP_TRACKING, seekBar.getProgress())); } @Override public void onStartTrackingTouch(SeekBar seekBar) { !/*no op!*/ } @Override protected void onDispose() { seekBar.setOnSeekBarChangeListener(null); } } Source(ish): JakeWharton/RxBinding -
Anatomy of a Custom Operator 1. construct 2. subscribeActual(Observer<?> observer)
3. inner listener... a. implements non-Reactive class b. implements Observer<?> 26 -
Map+Filter Hybrid: main class 27 public class ObservableFilterMap<In, Out> extends
Observable<Out> { private final Function<In, Out> mapper; private final Predicate<In> filter; public ObservableFilterMap(Predicate<In> filter, Function<In, Out> mapper) { this.filter = filter; this.mapper = mapper; } @Override protected void subscribeActual(Observer<? super Out> observer) { new MapFilterObserver!<>(observer, filter, mapper); } !!... } -
Map+Filter Hybrid: Inner Skeleton 28 static final class MapFilterObserver<In, Out>
implements Observer<In>, Disposable{ MapFilterObserver(Observer<Out> child, Predicate<In> filter, Function<In, Out> mapper) { !!... } @Override public void onSubscribe(@NonNull Disposable d) { !!... } @Override public void dispose() { !!... } @Override public boolean isDisposed() { !!... } @Override public void onNext(@NonNull In in) { !!... } @Override public void onError(@NonNull Throwable e) { !!... } @Override public void onComplete() { !!... } } -
Map+Filter Hybrid: Handling Subscription 29 static final class MapFilterObserver<In, Out>
implements Observer<In>, Disposable{ !!... private Disposable d; MapFilterObserver(Observer<Out> downstream, Predicate<In> filter, Function<In, Out> mapper) {!!...} @Override public void onSubscribe(@NonNull Disposable d) { if (DisposableHelper.validate(this.d, d)) { this.d = d; observer.onSubscribe(this); } } @Override public void dispose() { d.dispose(); } @Override public boolean isDisposed() { return d.isDisposed(); } -
Map+Filter Hybrid: Handling Disposal 30 static final class MapFilterObserver<In, Out>
implements Observer<In>, Disposable{ !!... private Disposable d; MapFilterObserver(Observer<Out> downstream, Predicate<In> filter, Function<In, Out> mapper) {!!...} @Override public void dispose() { d.dispose(); } @Override public boolean isDisposed() { return d.isDisposed(); } @Override public void onSubscribe(@NonNull Disposable d) { if (DisposableHelper.validate(this.d, d)) { this.d = d; observer.onSubscribe(this); } } -
Map+Filter Hybrid: Terminal Conditions 31 static final class MapFilterObserver<In, Out>
implements Observer<In>, Disposable{ !!... @Override public void onError(@NonNull Throwable e) { downstream.onError(e); } @Override public void onComplete() { downstream.onComplete(); } protected final void fail(Throwable t) { Exceptions.throwIfFatal(t); s.dispose(); onError(t); } } -
Map+Filter Hybrid: OnNext 32 static final class MapFilterObserver<In, Out> implements
Observer<In>, Disposable{ !!... @Override public void onNext(@NonNull In t) { boolean test; try { test = filter.test(t); } catch (Throwable e) { fail(e); return; } if (test) { Out out; try { out = ObjectHelper.requireNonNull(mapper.apply(t), "Mapper returned null!"); } catch (Throwable ex) { fail(ex); return; } downstream.onNext(out); } } } -
Putting it all together... 33 public Disposable trivium() { return
new ClickObservable(view) .map(b !-> "the quick brown fox jumped over the lazy dogs") .flatMapIterable(sentence !-> Arrays.asList(sentence.split("\)s"))) .flatMap(letter !-> new ObservableFilterMap<String, String>( l !-> l.length() > 0, String!::toUpperCase)) .distinct() .sorted() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .zipWith(Observable.range(1, 100), (letter, count) !-> String.format("%2d. %s", count, letter)) .subscribe(System.out!::println, Throwable!::printStackTrace); } -
Putting it all together... 34 public Disposable trivium() { return
new ClickObservable(view) .map(b !-> "the quick brown fox jumped over the lazy dogs") .flatMapIterable(sentence !-> Arrays.asList(sentence.split("\)s"))) .flatMap(letter !-> new ObservableFilterMap<String, String>( l !-> l.length() > 0, String!::toUpperCase)) .distinct() .sorted() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .zipWith(Observable.range(1, 100), (letter, count) !-> String.format("%2d. %s", count, letter)) .subscribe(System.out!::println, Throwable!::printStackTrace); } -
Putting it all together... 35 public Disposable trivium() { return
new ClickObservable(view) .map(b !-> "the quick brown fox jumped over the lazy dogs") .flatMapIterable(sentence !-> Arrays.asList(sentence.split("\)s"))) .flatMap(letter !-> new ObservableFilterMap<String, String>( l !-> l.length() > 0, String!::toUpperCase)) .distinct() .sorted() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .zipWith(Observable.range(1, 100), (letter, count) !-> String.format("%2d. %s", count, letter)) .subscribe(System.out!::println, Throwable!::printStackTrace); } -
Find the bug 37 Observable<Boolean> clickRx(@IdRes int id) { View
button = findViewById(id); return Observable.create(emitter !-> { button.setOnClickListener(v !-> emitter.onNext(Boolean.TRUE)); }); } -
Find the bug 38 Observable<Boolean> clickRx(@IdRes int id) { View
button = findViewById(id); return Observable.create(emitter !-> { button.setOnClickListener(v !-> emitter.onNext(Boolean.TRUE)); emitter.setDisposable(Disposables.fromAction(() !-> button.setOnClickListener(null))); }); } -
Find the bug 39 Observable<Boolean> clickRx(@IdRes int id) { View
button = findViewById(id); return Observable.create(emitter !-> { button.setOnClickListener(v !-> emitter.onNext(Boolean.TRUE)); emitter.setDisposable(Disposables.fromAction(() !-> button.setOnClickListener(null))); }); } -
Remember Me? 40 final class ClickObservable extends Observable<Boolean> { private
final View view; ClickObservable(View view) { this.view = view; } @Override protected void subscribeActual(Observer<? super Boolean> observer) { Listener listener = new Listener(view, observer); observer.onSubscribe(listener); view.setOnClickListener(listener); } !!... Source(ish): JakeWharton/RxBinding void onSubscribe(@NonNull Disposable d); -
Decisions, decisions... 41 Separate Class w/ inner class Observable!::create handling
Disposable is mandatory minimum 30 lines of (Java) code Literally 3 lines of code Memory leaks...maybe. -
Great power, great responsibility When you have a hammer, everything
looks like a nail 42 • Rx is a tool • custom operators: one tool in your toolkit • really bad: business logic • "meh": combining existing operators (Transformers: "Don't Break the Chain" - Dan Lew) • good: bridge your non-Rx streams into Rx -
Things I haven't covered...yet! • Operator fusion (inlining) • Serializing
onNext + terminal events (concurrency) • Backpressure strategies (source + intermediate operators) • request accounting • queue-draining strategies 43 -
Links/Sources/Inspiration/Future Reading 44 • Backpressure (Karnok) bit.ly/2GymQJl • JakeWharton/RxBinding •
ViewClickObservable • SeekBarChangeObservable bit.ly/2GvXLmi bit.ly/2ICy4wQ bit.ly/2qjiRKz • Writing Operators (Karnok) bit.ly/2H7wS5b • Operator Fusion (1 of 4) (Karnok) bit.ly/2GwMBxF • Map+Filter hybrid (Gist) bit.ly/2q2XTOR • Presentation.this bit.ly/2lBsBge