RxJava - YOW! Conferences · What is Reactive programming? Reactive programming is a programming...

Post on 21-May-2020

31 views 0 download

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?    }  });

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() {}  });

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 / jeroen@tietema.net