Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ReactiveX in Android
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Richard Cirerol
March 19, 2016
Programming
73
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ReactiveX in Android
An introduction to RxJava and RxAndroid (v1) and Rx patterns
Richard Cirerol
March 19, 2016
More Decks by Richard Cirerol
See All by Richard Cirerol
Unconventional Android - Dependency Injection with Dagger
codeprogression
0
110
Introduction to Kotlin, Pt. 1
codeprogression
0
130
Android Studio Tips
codeprogression
0
100
Android: Testing Reactive MVP Applications
codeprogression
0
180
Unconventional Android v3
codeprogression
0
46
Unconventional Android v2
codeprogression
3
430
A Brief History of the Internet
codeprogression
1
500
Other Decks in Programming
See All in Programming
Dockerfile CMD for Node.js
grazie1999
0
130
Oxlintはいいぞ(続)
yug1224
1
460
異なる設計思想のフレームワークを経験して得た学び
amekuhideki
1
710
進化を続けるGo toolsの現在地 / The Current State of Ever-Evolving Go Tools
hond0413
0
260
引き算の組織 ― アウトカムとAIに全振りするために辞めたこと ― / Organization by Subtraction
hirokiyamamoto14
PRO
0
300
in-process GraphQL のすすめ #ginzajs
izumin5210
4
1.5k
Flow は今どうなっているか
mizdra
PRO
0
740
go-spidermonkeyでAIエージェントのCode Modeを実装する
syumai
3
1.2k
Cloudflare is Agents
chimame
0
180
まだ間に合う!今年の夏こそSchemeのマクロ展開器を完全理解!
omasanori
0
530
T3DD26: From RAGs to Riches
martinhelmich
0
100
DynamoDBの基礎を振り返りながらベクトル検索機能を理解する
musan
2
230
Featured
See All Featured
How to build a perfect <img>
jonoalderson
1
5.9k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
450
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
Ten Tips & Tricks for a 🌱 transition
stuffmc
0
180
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.9k
For a Future-Friendly Web
brad_frost
183
10k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.8k
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
510
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
530
Code Review Best Practice
trishagee
74
20k
Navigating Team Friction
lara
192
16k
Transcript
RxAndroid Using ReactiveX in Android RxAndroid | Richard Cirerol |
@codeprogression
Who am I? RxAndroid | Richard Cirerol | @codeprogression
Richard Cirerol @codeprogression http://codeprogression.com RxAndroid | Richard Cirerol | @codeprogression
Sr. Android Engineer at MLBAM RxAndroid | Richard Cirerol |
@codeprogression
RxAndroid | Richard Cirerol | @codeprogression
http://bit.ly/mlbam-theverge RxAndroid | Richard Cirerol | @codeprogression
What is ReactiveX? - http://reactivex.io RxAndroid | Richard Cirerol |
@codeprogression
The Observer pattern done right 4 Observer pattern 4 Iterator
pattern 4 Functional programming 4 Asynchronous RxAndroid | Richard Cirerol | @codeprogression
Reactive extensions originated in .NET (circa 2010) RxAndroid | Richard
Cirerol | @codeprogression
RxJava is a port of Rx.NET started by Netflix (circa
2013) RxAndroid | Richard Cirerol | @codeprogression
Languages - Java: RxJava - JavaScript: RxJS - C#: Rx.NET
- C#(Unity): UniRx - Scala: RxScala - Clojure: RxClojure - C++: RxCpp - Ruby: Rx.rb - Python: RxPY - Groovy: RxGroovy - JRuby: RxJRuby - Kotlin: RxKotlin - Swift: RxSwift ReactiveX for platforms and frameworks - RxNetty - RxAndroid - RxCocoa RxAndroid | Richard Cirerol | @codeprogression
RxJava = Observables + Operators + Schedulers RxAndroid | Richard
Cirerol | @codeprogression
RxAndroid = RxJava + Additional Schedulers RxAndroid | Richard Cirerol
| @codeprogression
RxJava = Observables + Operators + Schedulers RxAndroid | Richard
Cirerol | @codeprogression
Observable 4 Emits a stream of items to observers 4
Notifies observers of termination events 4 Error 4 Completed RxAndroid | Richard Cirerol | @codeprogression
Observable.just(0); RxAndroid | Richard Cirerol | @codeprogression
Observable.just(0); Observable.just(0, 1, 2); RxAndroid | Richard Cirerol | @codeprogression
// 5, 6, 7, 8, 9 Observable.range(5,5) RxAndroid | Richard
Cirerol | @codeprogression
Integer[] items = new Integer[]{ 2, 4, 6, 8, 10
} Observable.from(items); RxAndroid | Richard Cirerol | @codeprogression
Observable.create(new OnSubscribe<Integer>() @Override public void call(Observer Observer){} try{ for (int
i = 0; i < 5; i++){ observer.onNext(i); } observer.onCompleted() } catch(Exception e){ observer.onError(e); } } }); RxAndroid | Richard Cirerol | @codeprogression
----------------------------> RxAndroid | Richard Cirerol | @codeprogression
----1-----------------------> RxAndroid | Richard Cirerol | @codeprogression
----1----20-----------------> RxAndroid | Richard Cirerol | @codeprogression
----1----20---17------------> RxAndroid | Richard Cirerol | @codeprogression
----1----20---17---8--------> RxAndroid | Richard Cirerol | @codeprogression
----1----20---17---8----|---> RxAndroid | Richard Cirerol | @codeprogression
----1----20---17---8----X---> RxAndroid | Richard Cirerol | @codeprogression
Observer 4 Subscribes to an Observable 4 Reacts to emitted
items 4 Reacts to termination events RxAndroid | Richard Cirerol | @codeprogression
public interface Observer<T> { void onNext(T item); void onError(Throwable t);
void onCompleted(); } RxAndroid | Richard Cirerol | @codeprogression
Single-threaded? Multi-threaded? RxAndroid | Richard Cirerol | @codeprogression
Synchronous? Asynchronous? RxAndroid | Richard Cirerol | @codeprogression
From a DB? A web service? A singleton? RxAndroid |
Richard Cirerol | @codeprogression
Who cares? RxAndroid | Richard Cirerol | @codeprogression
Observer to Observable: Just tell me when you've got something
new And tell me when you're done RxAndroid | Richard Cirerol | @codeprogression
Listener vs. Observable RxAndroid | Richard Cirerol | @codeprogression
Listener 4 Registration managed by the parent of the target
4 Callback interfaces are custom per implementation RxAndroid | Richard Cirerol | @codeprogression
// Before calling... service.getFoo(); // First call one of... service.setListener(this);
service.addListener(this); // And implement... public interface Listener { void onFooChanged(Foo foo); } // And remember to clean up... service.setListener(null); service.removeListener(this); RxAndroid | Richard Cirerol | @codeprogression
Observables 4 Registration managed internally by the Observable 4 Subscription
interface is consistent RxAndroid | Richard Cirerol | @codeprogression
// A Subscriber is a subclass of Observer Subscription subscription
= service.getFoo() .subscribe(new Subscriber<Foo>(){ @Override public void onNext(Foo item){ doSomething(item); } @Override public void onError(Throwable e){ Log.e(e); } @Override public void onCompleted(){ hideProgress(); } }); RxAndroid | Richard Cirerol | @codeprogression
Subscription subscription = service.getFoo() .subscribe(new Action1<Foo>(){ @Override public void call(Foo
foo){ doSomething(foo)); } }); RxAndroid | Richard Cirerol | @codeprogression
Subscription subscription = service.getFoo() .subscribe(new Action1<Foo>(){ @Override public void call(Foo
foo){ doSomething(foo)); } }, new Action<Throwable>(){ @Override public void call(Throwable t){ Log.e(t); } }); RxAndroid | Richard Cirerol | @codeprogression
// Still need to cleanup subscription.unsubscribe(); RxAndroid | Richard Cirerol
| @codeprogression
RxJava = Observables + Operators + Schedulers RxAndroid | Richard
Cirerol | @codeprogression
Operators 4 Are a specialized observer 4 Can transform observables
4 Can be chained 4 A function that takes a Subscriber and returns a Subscriber RxAndroid | Richard Cirerol | @codeprogression
Observable.from({0, 1, 2, 3, 4}) .subscribe(); RxAndroid | Richard Cirerol
| @codeprogression
Observable.from({0, 1, 2, 3, 4}) .skip(1) .map(new Func1<Integer, Integer>(){ @Override
public Integer call(Integer in){ return in^2; } }) .subscribe(); RxAndroid | Richard Cirerol | @codeprogression
Observable.from({0, 1, 2, 3, 4}) .doOnNext(new Action1<Integer>(){ @Override public void
call(Integer in){ return Log.i("Received: " + in); } }) .subscribe(); RxAndroid | Richard Cirerol | @codeprogression
Observable<Player> player; Observable<List<Team>> teams; Observable.combineLatest(player, teams, new Func2<Player, List<Team>, ViewModel>(){
@Override public ViewModel(Player player, List<Team> teams){ Team team = teams.get(player.getTeamId()); return new ViewModel(player.getName(), team.getName()); } }) .subscribe(...); RxAndroid | Richard Cirerol | @codeprogression
Observables are asynchronous by default A subscription will not block
the thread when waiting for a value. RxAndroid | Richard Cirerol | @codeprogression
toBlocking() makes an Observable synchronous A subscription WILL block the
thread when waiting for a value. RxAndroid | Richard Cirerol | @codeprogression
RxJava = Observables + Operators + Schedulers RxAndroid | Richard
Cirerol | @codeprogression
Normally, operations are done on the current thread RxAndroid |
Richard Cirerol | @codeprogression
Schedulers RxJava allows you to marshall operations to another thread.
RxAndroid | Richard Cirerol | @codeprogression
Schedulers subscribeOn() and observeOn() are special operators RxAndroid | Richard
Cirerol | @codeprogression
subscribeOn() declares the starting Scheduler RxAndroid | Richard Cirerol |
@codeprogression
observeOn() declares the continuing Scheduler RxAndroid | Richard Cirerol |
@codeprogression
RxJava Default Schedulers RxAndroid | Richard Cirerol | @codeprogression
Schedulers.immediate() RxAndroid | Richard Cirerol | @codeprogression
Schedulers.trampoline() RxAndroid | Richard Cirerol | @codeprogression
Schedulers.newThread() RxAndroid | Richard Cirerol | @codeprogression
Schedulers.computation() RxAndroid | Richard Cirerol | @codeprogression
Schedulers.io() RxAndroid | Richard Cirerol | @codeprogression
Schedulers.test() RxAndroid | Richard Cirerol | @codeprogression
Schedulers.from(Executor executor) RxAndroid | Richard Cirerol | @codeprogression
RxAndroid = RxJava + Additional Schedulers RxAndroid | Richard Cirerol
| @codeprogression
AndroidSchedulers.mainThread() RxAndroid | Richard Cirerol | @codeprogression
HandlerScheduler.create(Looper looper) RxAndroid | Richard Cirerol | @codeprogression
Observable.create(s -> getFooFromWeb()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(); RxAndroid | Richard Cirerol
| @codeprogression
Observable.create(s -> getFooFromWeb()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map(foo -> calculateBar(foo)) // !!
.subscribe(); RxAndroid | Richard Cirerol | @codeprogression
Observable.create(() -> getFooFromWeb()) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .map(foo -> calculateBar(foo)) .observeOn(AndroidSchedulers.mainThread()) .subscribe();
RxAndroid | Richard Cirerol | @codeprogression
Observable.create(s -> getFooFromWeb()) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .map(foo -> calculateBar(foo)) .observeOn(AndroidSchedulers.mainThread()) .subscribe();
RxAndroid | Richard Cirerol | @codeprogression
Let's break this up RxAndroid | Richard Cirerol | @codeprogression
// in publisher class public Observable<Bar> getBar(){ return Observable.create(s ->
getFooFromWeb()) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .map(foo -> calculateBar(foo)) } // in subscriber class (presenter?) getBar() .observeOn(AndroidSchedulers.mainThread()); .subscribe(item -> updateView(item)); RxAndroid | Richard Cirerol | @codeprogression
private Bar cachedBar; public Observable<Bar> getBar(){ return Observable.create(s -> getFooFromWeb())
.subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .map(foo -> calculateBar(foo)) .doOnNext(bar -> cachedBar = bar) } RxAndroid | Richard Cirerol | @codeprogression
private Bar cachedBar; public Observable<Bar> getBar(){ if (cachedBar != null){
return Observable.just(cachedBar); } else { return Observable.create(s -> getFooFromWeb()) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .map(foo -> calculateBar(foo)) .doOnNext(bar -> cachedBar = bar) } } RxAndroid | Richard Cirerol | @codeprogression
Rx in the Android Lifecycle RxAndroid | Richard Cirerol |
@codeprogression
public void onResume(){ subscription = publisher.getFoo().subscribe(...); } RxAndroid | Richard
Cirerol | @codeprogression
public void onResume(){ subscription = publisher.getFoo().subscribe(...); } public void onPause(){
subscription.unsubscribe(); } RxAndroid | Richard Cirerol | @codeprogression
public void onResume(){ subscription1 = publisher.getFoo().subscribe(...); subscription2 = publisher.getBar().subscribe(...); subscription3
= publisher.getBaz().subscribe(...); } public void onPause(){ subscription1.unsubscribe(); subscription2.unsubscribe(); subscription3.unsubscribe(); } RxAndroid | Richard Cirerol | @codeprogression
CompositeSubscription subscriptions; public void onResume(){ subscriptions = new CompositeSubscription(); subscriptions.add(publisher.getFoo().subscribe(...));
subscriptions.add(publisher.getBar().subscribe(...)); subscriptions.add(publisher.getBaz().subscribe(...)); } public void onPause(){ subscriptions.unsubscribe(); } RxAndroid | Richard Cirerol | @codeprogression
Warning Once unsubscribed, a CompositeSubscription will not manage any new
subscriptions. RxAndroid | Richard Cirerol | @codeprogression
Case Study RxAndroid | Richard Cirerol | @codeprogression
Case Study Problem: Retrieve a video url for a program
RxAndroid | Richard Cirerol | @codeprogression
Case Study Problem: Retrieve a video url for a program
RxAndroid | Richard Cirerol | @codeprogression
Intent intent = new Intent(this, VideoPlayerActivity.class); intent.putExtra("PROGRAM_ID", programId); startActivity(intent); RxAndroid
| Richard Cirerol | @codeprogression
String id = getIntent().getStringExtra("PROGRAM_ID", id); subscription = publisher.getVideoUrl(id) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new
Subscriber<String>(){ @Override public void onCompleted() {} @Override public void onError(final Throwable e) { } @Override public void onNext(final String url) { player.load(url); } }); RxAndroid | Richard Cirerol | @codeprogression
Retrofit 4 http://square.github.io/retrofit 4 Type-safe HTTP client 4 RxJava capable
(with adapter) RxAndroid | Richard Cirerol | @codeprogression
public interface VideoApi { @GET("/video/{id}") Observable<Video> getVideo(@Path("id") String id); }
RxAndroid | Richard Cirerol | @codeprogression
// VideoService.java public Observable<String> getVideoUrl(String id){ api.getVideo() // return a
url from the Video object } RxAndroid | Richard Cirerol | @codeprogression
map() Transform each item from one value to another RxAndroid
| Richard Cirerol | @codeprogression
// VideoService.java public Observable<String> getVideoUrl(String id){ api.getVideo() .map(new Func1<Video, String>{
@Override public String call(Video video){ return video.getUrl(); } }); } RxAndroid | Richard Cirerol | @codeprogression
// VideoService.java public Observable<String> getVideoUrl(String id){ api.getVideo() .subscribeOn(Schedules.io) .map(new Func1<Video,
String>(){ @Override public String call(Video video){ return video.getUrl(); } }); } RxAndroid | Richard Cirerol | @codeprogression
Web service calls aren't that simple. RxAndroid | Richard Cirerol
| @codeprogression
public interface VideoApi { @GET("/video/{id}") Observable<Video> getVideo( @Header("Authorization") auth, @Path("id")
String id); } RxAndroid | Richard Cirerol | @codeprogression
public interface VideoApi { @GET("/video/{id}") Observable<Video> getVideo( @Header("Authorization") auth, @Path("id")
String id); @POST("/token") Observable<String> getToken(@Body TokenRequest request); } RxAndroid | Richard Cirerol | @codeprogression
// VideoService.java public Observable<String> getVideoUrl(String id){ api.getToken(tokenRequest) .subscribeOn(Schedules.io) ... api.getVideo()
.map(new Func1<Video, String>{ @Override public String call(Video video){ return video.getUrl(); } }); } RxAndroid | Richard Cirerol | @codeprogression
Don't break the chain 4 Use operators to transform RxAndroid
| Richard Cirerol | @codeprogression
flatMap() Transform each item into one or more Observables, then
flatten RxAndroid | Richard Cirerol | @codeprogression
// VideoService.java private TokenRequest request; public Observable<String> getVideoUrl(final String id){
api.getToken(tokenRequestBody) .subscribeOn(Schedules.io) .flatMap(new Func1<String, Observable<Video>(){ @Override public Observable<Video> call(String token){ return api.getVideo(token, id); } }) .map(new Func1<Video, String>(){ @Override public String call(Video video){ return video.getUrl(); } }); } RxAndroid | Richard Cirerol | @codeprogression
RxAndroid = Observables + Operators + Schedulers + Additional Schedulers
RxAndroid | Richard Cirerol | @codeprogression
Observables 4 stream data to observers 4 also notify observers
of termination events RxAndroid | Richard Cirerol | @codeprogression
Operators 4 transform data 4 provide side effects 4 affect
either 4 items 4 observable RxAndroid | Richard Cirerol | @codeprogression
Schedulers 4 enable concurrency strategies RxAndroid | Richard Cirerol |
@codeprogression
Subscribers 4 act on the emitted items and notifications 4
are protected from synchronicity or concurrency concerns RxAndroid | Richard Cirerol | @codeprogression
http://reactivex.io http://http://blog.danlew.net RxAndroid | Richard Cirerol | @codeprogression
Richard Cirerol @codeprogression http://codeprogression.com RxAndroid | Richard Cirerol | @codeprogression
Thanks! RxAndroid | Richard Cirerol | @codeprogression