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
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
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Parth Padgaonkar - RxJava Extended
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
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
89
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
92
Boris Farber & Nikita Kozlov - The_Build_Side_of_Android_App
droidcon_berlin_2018
0
230
Zan Markan - The state of Kotlin
droidcon_berlin_2018
0
90
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
340
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
JIS keyboard support in Omarchy
lef237
0
120
Optunaを作ったときにやりたかったこと、その後 (2026/10/16, Optuna Meetup #3)
iwiwi
1
440
Web Framework Benchmark Battle 2026, Any%
a_matsuda
2
2.2k
動作中のプログラムの中身をリアルタイムに覗く / Realtime Debugger for CSharp with Roslyn
prota
1
1.9k
Agents on Rails - Rails at Scale 2026
irinanazarova
0
560
[ハンズオン]AIへの指示だけで「五目並べ」を作ってみよう
satoshi256kbyte
1
380
作って学ぶSpring BootのAuto Configuration / JJUG Night Seminar 2026-10-19
rshindo
0
150
Railsバージョンを4倍にした話
optim
0
420
Workers Cache を知る
syumai
0
370
當我們開始不讀 Code - AI Agent 時代,Code Review 與 Clean Code 還剩下什麼?
jame2408
1
240
【加筆修正版】Laravel のアプリケーションをどこにデプロイするか #phpcon_ehime
akase244
0
150
dotNET_vulnclicker_intro.pdf
deka_morita
0
140
Featured
See All Featured
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.9k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.8k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
3k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
940
Accessibility Awareness
sabderemane
1
250
AI Search: Where Are We & What Can We Do About It?
aleyda
0
8.1k
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
570
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
370
Fantastic passwords and where to find them - at NoRuKo
philnash
53
4k
Heart Work Chapter 1 - Part 1
lfama
PRO
10
37k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
380
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
590
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]