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
Richard Cirerol
March 19, 2016
Programming
0
56
ReactiveX in Android
An introduction to RxJava and RxAndroid (v1) and Rx patterns
Richard Cirerol
March 19, 2016
Tweet
Share
More Decks by Richard Cirerol
See All by Richard Cirerol
Unconventional Android - Dependency Injection with Dagger
codeprogression
0
100
Introduction to Kotlin, Pt. 1
codeprogression
0
110
Android Studio Tips
codeprogression
0
82
Android: Testing Reactive MVP Applications
codeprogression
0
150
Unconventional Android v3
codeprogression
0
31
Unconventional Android v2
codeprogression
3
410
A Brief History of the Internet
codeprogression
1
400
Other Decks in Programming
See All in Programming
Goのmultiple errorsについて (2024年4月版)
syumai
4
1.2k
Ruby GitHub Packages
bkuhlmann
0
640
大規模Reactアプリのリアーキテクチャ~8万行のTanStack Query移行の軌跡~
kj455
4
1k
Compose-View Interop in Practice (mDevCamp 2024)
stewemetal
0
170
CA.swift19 恋するAIアプリ開発の裏側
oskmr
0
380
障害対応を起点としたもっといい開発と運用のサイクル作りのためにできること / Hatena Enginner Seminar #29
polamjag
0
370
Fast JSX: Don't clone props object #28768
yossydev
1
160
スクラムガイドのスプリントレトロスペクティブを改めて読みかえしてみた / Re-reading the Sprint Retrospective Section in the Scrum Guide
mackey0225
3
480
初心者のためのRubyKaigi入門/RubyKaigi Introduction
a_matsuda
8
1.4k
Java 22 Overview
kishida
1
190
AmperとFleetを使ったAndroidアプリ
yoppie
0
250
Build Apps for iOS, Android & Desktop in 100% Kotlin With Compose Multiplatform (mDevCamp 2024)
zsmb
0
430
Featured
See All Featured
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
123
39k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
242
1.2M
The Mythical Team-Month
searls
216
42k
Designing Experiences People Love
moore
136
23k
Typedesign – Prime Four
hannesfritz
36
2.1k
Put a Button on it: Removing Barriers to Going Fast.
kastner
58
3.1k
The Invisible Side of Design
smashingmag
294
49k
The Cost Of JavaScript in 2023
addyosmani
21
3.9k
Automating Front-end Workflow
addyosmani
1357
200k
A Modern Web Designer's Workflow
chriscoyier
689
190k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
155
14k
Mobile First: as difficult as doing things right
swwweet
217
8.6k
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