Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Parth Padgaonkar - RxJava Extended
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
droidcon Berlin
July 17, 2018
Programming
170
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Parth Padgaonkar - RxJava Extended
droidcon Berlin
July 17, 2018
More Decks by droidcon Berlin
See All by droidcon Berlin
Jon Markoff - Best practice for apps
droidcon_berlin_2018
0
230
Jon Markoff - Voice in the enterprise
droidcon_berlin_2018
0
86
Michael Jess - Enabling enterprise mobility with SAP
droidcon_berlin_2018
0
140
Ronen Sabag - Lean async code with Kotlin’s coroutines
droidcon_berlin_2018
0
89
Boris Farber & Nikita Kozlov - The_Build_Side_of_Android_App
droidcon_berlin_2018
0
220
Zan Markan - The state of Kotlin
droidcon_berlin_2018
0
88
Miquel Beltran - No More □ (tofu) Mastering Emoji on Android
droidcon_berlin_2018
0
150
Laurent Gasser & Jeremy Rochot - Sharing a success story - A low cost, Customer driven and co-developed Android EMM
droidcon_berlin_2018
0
330
Hoi Lam - Adding ML Kit to Android Things And some TensorFlow things
droidcon_berlin_2018
1
250
Other Decks in Programming
See All in Programming
Kiroで創り、AgentCoreで繋ぐ!AWSで実践する「AI-DLC」から「AIエージェント統合」までの最新地図
licux
3
360
PyO3 で既存 Python 評価器を Rust core 化する ー wasm-bindgen でブラウザにも配るための設計
kdash
1
560
Deep dive into the select statement (GopherCon UK)
jespino
0
170
AIエージェント時代のコードレビューを設計する
nogu66
6
2.6k
新卒PdEのリアル
ryu1013
1
440
Family mrubyの進捗
kishima
1
100
AIに既存システムを理解させる技術 ~レガシーを見捨てないハーネスエンジニアリング入門~
ochtum
0
210
PHPプロジェクトの結合バランスを可視化する #php_night
kajitack
0
230
DroidKaigi 2026 「個人開発という実験場: Android エンジニアが手にする4つの自由」
slashnephy
0
220
アクセシビリティから考える情報設計
high_g_engineer
0
320
ALB ログから Trace を気合で繋げる技術
fohte
7
880
Snowflakeで業務アプリを作ろう。 Snowflakeのアプリ機能解説&実践ガイド
ayumu_yamaguchi
1
160
Featured
See All Featured
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
4
600
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
240
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
HDC tutorial
michielstock
2
850
Rails Girls Zürich Keynote
gr2m
96
14k
Visualization
eitanlees
152
17k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
Building the Perfect Custom Keyboard
takai
2
870
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
Paper Plane
katiecoart
PRO
2
53k
For a Future-Friendly Web
brad_frost
183
10k
Transcript
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
2
3
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
What is RxJava? 6
RxJava is... • asynchronous (sometimes!) • event-based stream • composable
• backpressure-sensitive 7
Upstream emits faster than downstream can consume Backpressure? 8
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? 10
Q: Do I really care about backpressure? A: Probably not?
Q: Do I really care about backpressure? A: Probably not?
(I don't know your life)
Part II: Extending RxJava 13
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); }
Part III: Observable!::create (and other pitfalls) 36
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
45 Parth Padgaonkar
[email protected]