RxJava - YOW! Conferences · What is Reactive programming? Reactive programming is a programming...
Transcript of RxJava - YOW! Conferences · What is Reactive programming? Reactive programming is a programming...
RxJavaand WHY you should care
Jeroen Tietema
Overviewintro ofRxJavawhy?some tips
What is RxJava?Java implementation of Reactive ExtensionsReactive Extensions is .Net implementation of ReactiveProgramming
What is Reactive programming?Reactive programming is a programming paradigmoriented around data ᴀ밄ows and the propagation of
change.
This means that it should be possible to express staticor dynamic data ᴀ밄ows with ease in the programming
languages used, and that the underlying executionmodel will automatically propagate changes through
the data ᴀ밄ow. – Wikipedia (Reactive Programming)
TLDR; something with data ᴀ밄ows?
What is Reactive Extensions?Using Rx, developers represent asynchronous data
streams with Observables, query asynchronous datastreams using LINQ operators, and parameterize theconcurrency in the asynchronous data streams using
Schedulers.
Simply put, Rx = Observables + LINQ + Schedulers. –Microsoft Reactice Extensions Homepage
RxJava conceptsObservables and the ObservableContractSubscribing and subscriptionsoperators
ObservablesObservables emit data when you subscribe to them.
onNext (0..∞)onError (0..1)onCompleted(0..1)
Cannot call both onError and onCompleted.
Should never do any calls after the Subscriber unsubscribes.
When multiple observers subscribe to the same Observable, it is up tothe Observable to decide what to do with even emission.
Subscribers and SubscriptionsObservable<Integer> myInts = Observable.just(1, 2, 3, 4, 5);
Subscribers and SubscriptionsObservable<Integer> myInts = Observable.just(1, 2, 3, 4, 5);
Subscription s = myInts.subscribe(new Subscriber<Integer>() { void onNext(Integer i) { // your Integer has arrived } void onCompleted() { // Happy Friday!!! } void onError(Throwable e) { // :'‐( }});
Subscribers and SubscriptionsObservable<Integer> myInts = Observable.just(1, 2, 3, 4, 5);
Subscription s = myInts.subscribe(new Subscriber<Integer>() { void onNext(Integer i) { // your Integer has arrived } void onCompleted() { // Happy Friday!!! } void onError(Throwable e) { // :'‐( }});// boring, boring!s.unsubscribe();
OperatorsObservable<Integer> myInts = Observable.just(1, 2, 3, 4, 5);
OperatorsObservable<Integer> myInts = Observable.just(1, 2, 3, 4, 5);
Observable<Integer> evenNumbers = myInts.filter(i ‐> i % 2 == 0)
OperatorsObservable<Integer> myInts = Observable.just(1, 2, 3, 4, 5);
Observable<Integer> evenNumbers = myInts.filter(i ‐> i % 2 == 0);
Observable<String> myIntsAsStrings = evenNumbers .map(i ‐> "My lovely int " + i);
OperatorsObservable<Integer> myInts = Observable.just(1, 2, 3, 4, 5);Observable<Integer> evenNumbers = myInts.filter(i ‐> i % 2 == 0);
Observable<String> myIntsAsStrings = evenNumbers .map(i ‐> "My lovely int " + i);// Subscribing results in:onNext("My lovely int 2");onNext("My lovely int 4");onCompleted();
Operators (continued)Observable<Integer> helloAsyncInts = Observable.just(1, 2, 3, 4); // start emitting on Schedulers.io() .subscribeOn(Schedulers.io()) // emit the results on main UI thread .observeOn(AndroidSchedulers.mainThread());
Operators (continued)Observable<Integer> helloAsyncInts = Observable.just(1, 2, 3, 4); // start emitting on Schedulers.io() .subscribeOn(Schedulers.io()) // emit the results on main UI thread .observeOn(AndroidSchedulers.mainThread());
You can call observeOn multiple times!AndroidSchedulers comes fromRxAndroid
Operators (continued)Observable<Integer> helloAsyncInts = Observable.just(1, 2, 3, 4); // add a delay to emitted items on // Schedulers.computation() .delay(500, TimeUnit.MILLISECONDS)
But WHY ?© Leonid Mamchenkov
Retrofit vs RxJava (async)myRetrofitApi.someCall( new Callback<SomePojo>() { void onSuccess(SomePojo myPojo) { // your awesomeness }
void onError(RetrofitError error) { // deal with it } });
Subscription s = myObservableApi.someCall() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<SomePojo>() { void onNext(SomePojo myPojo) { // your awesomeness } void onError(Throwable error) { // deal with it }
void onCompleted() { // but ... why? } });
Retrofit vs RxJava (sync)SomePojo myPojo = myRetrofitApi.someCall(); Subscription s = myObservableApi.someCall()
.subscribe(new Subscriber<SomePojo>() { void onNext(SomePojo myPojo) { // your awesomeness } void onError(Throwable error) { // deal with it }
void onCompleted() { // but ... why? } });
© Freddie Alequin
Threading dilemma (or not?)// syncSomePojo myPojo = myRetrofitApi.someCall();
Observable<SomePojo> myObservableApi.someCall();
// asyncmyRetrofitApi.someCall(myCallback);
Observable<SomePojo> myObservableApi.someCall();
Decisions around threading are big decisions, but we end up makingthem in a ad-hoc fashion
RxJava: You can worry about threading later!
For example
Observable<Account> observable = myDatabase.findAccount();
Observable<HomePageFeed> observable = myDatabase.findAccount() .flatMap(myAccount ‐> myApi.fetchHomePageFeed(myAccount))
Observable<HomePageFeed> observable = myDatabase.findAccount() .flatMap(myAccount ‐> myApi.fetchHomePageFeed(myAccount)) .doOnNext(myHomePageFeed ‐> myCache.store(myHomePageFeed))
Observable<HomePageFeed> observable = myDatabase.findAccount() .flatMap(myAccount ‐> myApi.fetchHomePageFeed(myAccount)) .doOnNext(myHomePageFeed ‐> myCache.store(myHomePageFeed)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread());
Subscription s = myDatabase.findAccount() .flatMap(myAccount ‐> myApi.fetchHomePageFeed(myAccount)) .doOnNext(myHomePageFeed ‐> myCache.store(myHomePageFeed)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<HomePageFeed>() { void onNext(HomePageFeed myHomePageFeed) { // render homepage feed } void onError(Throwable error) { // show error dialog } void onCompleted(){} });
Compared to:
myDatabase.findAccount(new DatabaseCallback<Account>() {
void onQueryComplete(Account account){ }});
myDatabase.findAccount(new DatabaseCallback<Account>() {
void onQueryComplete(Account account){ myApi.getHomePage(account, new ApiCallback<HomePageFeed>() { void onResponse(HomePageFeed homepage) { } }); }});
myDatabase.findAccount(new DatabaseCallback<Account>() {
void onQueryComplete(Account account){ myApi.getHomePage(account, new ApiCallback<HomePageFeed>() { void onResponse(HomePageFeed homepage) { myCache.store(homepage); } }); }});
myDatabase.findAccount(new DatabaseCallback<Account>() {
void onQueryComplete(Account account){ myApi.getHomePage(account, new ApiCallback<HomePageFeed>() { void onResponse(HomePageFeed homepage) { myCache.store(homepage); // <‐‐ Disk IO on UI thread! } }); }});
myDatabase.findAccount(new DatabaseCallback<Account>() {
void onQueryComplete(Account account){ myApi.getHomePage(account, new ApiCallback<HomePageFeed>() { void onResponse(HomePageFeed homepage) { new AsyncTask<Void, Void, Void>() { Void doInBackground(Void... voids) { myCache.store(homepage); return null; } }.execute(); } }); }});
Benefitswriting complicated threaded code becomes (relatively) easy.clear pattern to cancel background tasks throughSubscriptions
© rjp
Operators, operators, operators!!manipulating datacan do threadingbut also replace conditionallogic
For exampleSubscription s = myCache.fetchHomePageFeed() .concat(myApi.fetchHomePageFeed()) .first() .subscribe(new Subscriber // etc.
For exampleSubscription s = myCache.fetchHomePageFeed() .concat(myApi.fetchHomePageFeed()) .first() .subscribe(new Subscriber // etc.
vsHomePageFeed myFeed = myCache.fetchHomePageFeed();if (myFeed == null) { myFeed = myApi.fetchHomePageFeed();}return myFeed;
Search exampleclass MySearchView extends TextView {
PublishSubject<String> subject = PublishSubject.create();
void init() { addTextChangedListener(new TextWatcher() { // .. void afterTextChanged(Editable s) { subject.onNext(s.toString()); } }); }
Observable<String> getTextObservable() { return subject; }}
Search examplemySearchView.getTextObservable() .filter(text ‐> text.length > 2)
Search examplemySearchView.getTextObservable() .filter(text ‐> text.length > 2) .debounce(400, TimeUnit.MILLISECONDS)
Search examplemySearchView.getTextObservable() .filter(text ‐> text.length > 2) .debounce(400, TimeUnit.MILLISECONDS) .observeOn(Schedulers.io()) .flatMap(text ‐> myApi.search(text))
Search examplemySearchView.getTextObservable() .filter(text ‐> text.length > 2) .debounce(400, TimeUnit.MILLISECONDS) .observeOn(Schedulers.io()) .flatMap(text ‐> myApi.search(text)) .observeOn(AndroidSchedulers.mainThread())
Search exampleSubscription s = mySearchView.getTextObservable() .filter(text ‐> text.length > 2) .debounce(400, TimeUnit.MILLISECONDS) .observeOn(Schedulers.io()) .flatMap(text ‐> myApi.search(text)) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber() { void onNext(SearchResults results) { // render results } void onError(Throwable someError) {} void onCompleted() {} });
Search vanilla examplemyTextView.addTextChangedListener(new TextWatcher() {
void afterTextChanged(final Editable s) { if (s.toString().length > 2) {
} }});
Search vanilla examplemyTextView.addTextChangedListener(new TextWatcher() {
void afterTextChanged(final Editable s) { if (s.toString().length > 2) { myHandler.postDelayed(new Runnable() { @Override void run() {
} }, 400L); } }});
Search vanilla examplemyTextView.addTextChangedListener(new TextWatcher() {
void afterTextChanged(final Editable s) { if (s.toString().length > 2) { myHandler.postDelayed(new Runnable() { @Override void run() { myAsyncTask = new FetchSuggestionsTask(); myAsyncTask.execute(s.toString()); } }, 400L); } }});
Search vanilla examplemyTextView.addTextChangedListener(new TextWatcher() {
void afterTextChanged(final Editable s) { if (s.toString().length > 2) { if (myCallback != null) { myHandler.removeCallbacks(myCallback); } myCallback = new Runnable() { @Override void run() { myAsyncTask = new FetchSuggestionsTask(); myAsyncTask.execute(s.toString()); } } myHandler.postDelayed(myCallback, 400L); } }});
Search exampleSubscription s = mySearchView.getTextObservable() .filter(text ‐> text.length > 2) .debounce(400, TimeUnit.MILLISECONDS) .observeOn(Schedulers.io()) .flatMap(text ‐> myApi.search(text)) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber() { void onNext(SearchResults results) { // render results } void onError(Throwable someError) {} void onCompleted() {} });
© Craig Sunter
Creating Observablesclass MyObservableApi {
public Observable<HomePageFeed> getHomePage(Account account) { // wrong return Observable.just( mySyncLegacyApi.getHomePage(account)); }}
Defer + just = your friend!class MyObservableApi {
public Observable<HomePageFeed> getHomePage(Account account) { // right! return Observable.defer(() ‐> Observable.just( mySyncLegacyApi.getHomePage(account)); }}
Testing model layer@Testpublic void getHomePage_givenValidAccount_returnsHomePage() { TestSubscriber<HomePageFeed> s = new TestSubscriber<>(); myObservableApi.subscribe(s); s.assertCompleted(); s.assertValueCount(1); HomePageFeed h = s.getOnNextValues().get(0);}
Testing Presenter layer (Rx HQ)Inject your Schedulers
Schedulers.immediate() will su鍉䨈ce most of the time.
Use TestScheduler for more advanced use cases.
Testing time based logic@Testpublic void test400MilliesDelay() { TestScheduler testScheduler = new TestScheduler(); TestSubscriber<Integer> testSubscriber = new TestSubscriber<>();
Observable.just(1) .delay(400, TimeUnit.MILLISECONDS, testScheduler) .subscribe(testSubscriber);
testScheduler.advanceTimeBy(200, TimeUnit.MILLISECONDS); testSubscriber.assertNoValues();
testScheduler.advanceTimeBy(200, TimeUnit.MILLISECONDS); testSubscriber.assertValueCount(1); testSubscriber.assertCompleted();}
Useful linksFunctional Reactive Programming with RxJava, Ben Christensen,
Netᴀ밄ixhttps://www.youtube.com/watch?v=_t06LRX0DV0
Going Reactive, An Android Architectural Journey Matthias Käppler,Soundcloud
https://www.youtube.com/watch?v=R16OHcZJTno
Grokking RxJava, Dan Lew, Trellohttp://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/http://blog.danlew.net/2014/09/22/grokking-rxjava-part-2/http://blog.danlew.net/2014/09/30/grokking-rxjava-part-3/http://blog.danlew.net/2014/10/08/grokking-rxjava-part-4/
Questions?© Beatnik Photos
Thank youSlides are at:
https://jeroen.tietema.net/presentations/rxjava-yow/
Contact me at:@jtietema / [email protected]