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
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
月間100万人のサービス刷新で、 技術選定をHonoに振り切った話
yutobara
8
6.6k
Railsバージョンを4倍にした話
optim
0
380
Apple Intelligence を用いた個人情報誤送信防止、及びユーザーリクエスト体験の改善について
yukiny
0
350
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
370
PumaからFalconに移行したRailsアプリケーションでの実践
falcon8823
0
170
Rでドレミ/Do-Re-Mi_with_R
florets1
0
130
自サークルのIdPからログアウトできるようになるまで
taiseiue
1
140
コーディング自動化の次へ。人の介入をゼロにするファクトリーとサポートAIによる実践
timakin
5
3.6k
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
500
Atomic Design, Enforced: Scaling Mobile Design Systems (next.app devCon / droidCon Berlin 2026)
steliosf
PRO
0
170
ハーネス設計入門 〜 基礎知識の整理から実務へのステップアップ 〜
kinopeee
28
32k
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
1.2k
Featured
See All Featured
Claude Code のすすめ
schroneko
67
230k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
500
Designing for Performance
lara
612
70k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
56
3.5k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
460
We Have a Design System, Now What?
morganepeng
55
8.4k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
640
GitHub's CSS Performance
jonrohan
1033
470k
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
460
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
2
2.9k
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]