Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RxFy All The Things !

RxFy All The Things !

From Droidcon Paris 2014

Ever had to deal with terribly designed API or spent hours re-factoring your code to deal with an updated API?
You've heard about RxJava but can't see how it could benefit your project?
This talk will take the practical approach of a complex API to explain how RxJava and Functional Reactive Programming (FRP) can be used on every project to make your life easier.

6e2280dee0bb8970a3a0257b56a42f42?s=128

Benjamin AUGUSTIN

September 23, 2014
Tweet

Transcript

  1. RxFy All The Things! No pain and easy refactoring

  2. @Dorvaryn +BenjaminAugustin-Dorvaryn Dorvaryn Benjamin Augustin Android Software Craftsman

  3. None
  4. Why ?

  5. Development is hell

  6. So what is RxJava ?

  7. What is an Observable ? Single Multiple Sync T getData()

    Iterable<T> getData() Async Future<T> getData() Observable<T> getData
  8. What is an Observable ?

  9. What is an Observer ?

  10. Resource RxJava wiki all the help you need https://github.com/ReactiveX/RxJava/wiki

  11. Schematics

  12. map • Function • Transformation • For each element

  13. flatMap • Function • Composition • For each element

  14. onErrorResumeNext • Function • Recovery • On Failure

  15. Async control • subscribeOn(Scheduler) : ◦ Execute observable on Scheduler

    • observeOn(Scheduler) : ◦ Deliver results to observer on Scheduler
  16. None
  17. All is starting well • addToBasket ◦ Take a barcode

    ◦ Return a basket
  18. Easy enough right ? private class AddToBasket extends AsyncTask<Barcode, Void,

    Basket> { private final API api; private final Basket currentBasket; private final BasketViewUpdater updater; AddToBasket(API api, Basket currentBasket, BasketViewUpdater updater) { this.api = api; this.currentBasket = currentBasket; this.updater = updater; } @Override protected Basket doInBackground(Barcode... barcodes) { return api.addToBasket(barcodes[0]); } @Override protected void onPostExecute(Basket response) { super.onPostExecute(response); updater.updateWith(response); } }
  19. After 4 months... private class AddToBasket extends AsyncTask<Barcode, Void, AsyncResult<Response<Diff>>>

    { private final API api; private final Basket currentBasket; private final BasketViewUpdater updater; AddToBasket(API api, Basket currentBasket, BasketViewUpdater updater) { this.api = api; this.currentBasket = currentBasket; this.updater = updater; } @Override protected AsyncResult<Response<Diff>> doInBackground(Barcode... barcodes) { return AsyncResult.just(api.addToBasket(barcodes[0])); } @Override protected void onPostExecute(AsyncResult<Response<Diff>> response) { super.onPostExecute(response); new CheckForError(currentBasket, updater).execute(response); } } private class CheckForError extends AsyncTask<AsyncResult<Response<Diff>>, Void, AsyncResult<Diff>> { private final BasketViewUpdater updater; private final Basket currentBasket; CheckForError(Basket currentBasket, BasketViewUpdater updater) { this.currentBasket = currentBasket; this.updater = updater; } @Override protected AsyncResult<Diff> doInBackground(AsyncResult<Response<Diff>>... responses) { AsyncResult<Response<Diff>> responseAsyncResult = responses[0]; if(responseAsyncResult.isError()) { return AsyncResult.error(responseAsyncResult.error); } Response<Diff> response = responseAsyncResult.value; if (response.isSuccess()){ return AsyncResult.just(response.getValue()); } return AsyncResult.error(new CustomError()); } @Override protected void onPostExecute(AsyncResult<Diff> basket) { super.onPostExecute(basket); new ApplyDiff(currentBasket, updater).execute(basket); } } private class ApplyDiff extends AsyncTask<AsyncResult<Diff>, Void, AsyncResult<Basket>> { private final Basket currentBasket; private final BasketViewUpdater updater;
  20. So how to RxFy it ?

  21. Rx Style class BasketObserver implements Observer<Basket> { public onNext(Basket basket)

    { updateUI(basket); } public onError(Throwable error) { displayDialogFor(error); } public onComplete() { } }
  22. Rx Style Definition: Usage: public Observable<Basket> addToBasket(Barcode code); addToBasket(code) .subscribe(new

    BasketObserver());
  23. HTTP Standard ?

  24. Refactoring public Observable<Basket> addToBasket(Barcode code) { return addToBasketReq(code).flatmap(checkAPISuccess()); } private

    Observable<Response<Basket>> addToBasketReq(Barcode code); Definition: addToBasket(code) .subscribe(new BasketObserver()); Usage:
  25. flatMap • Function • Composition • For each element

  26. Function public Func1<Response<Basket>, Observable<Basket>> checkApiSuccess() { return new Func1<Response<Basket>, Observable<Basket>>()

    { @Override public Observable<Basket> call(Response<Basket> response) { if (response.isSuccess()) { return Observable.from(response.getBody()); } return Observable.error(new CustomError(header)); } }; }
  27. Stateless ?

  28. Refactoring public Observable<Basket> addToBasket(Barcode code) { return addToBasketReq(code) .flatmap(checkAPISuccess()) .map(applyDiffTo(cachedBasket));

    } Definition:
  29. map • Function • Transformation • For each element

  30. Function public Func1<Diff, Basket> applyDiffTo(final Basket basket) { return new

    Func1<Diff, Basket>() { @Override public Basket call(Diff diff) { return basket.apply(diff); } }; }
  31. Logic server side ?

  32. Refactoring public Observable<Basket> addToBasket(Barcode code) { return addToBasketReq(code) .flatmap(checkAPISuccess()) .flatmap(addCustomerIfNeeded())

    .map(applyDiffTo(cachedBasket)); } Definition:
  33. Function private Func1<Diff, Observable<Diff>> addCustomerIfNeeded() { return new Func1<Diff, Observable<Diff>>()

    { @Override public Observable<Diff> call(Diff diff) { if (diff.requireAddCustomer()) { return addCustomer(diff.getCustomerId()); } return Observable.from(diff); } }; }
  34. Oh what about the law ?

  35. UI in network flow ?

  36. Refactoring public Observable<Basket> addToBasket(Barcode code) { return addToBasket(code, Data.emptyData); }

    public Observable<Basket> addToBasket(Barcode code, Data data) { return addToBasketReq(code, data) .flatmap(checkAPISuccess()) .flatmap(addCustomerIfNeeded()) .map(applyDiffTo(cachedBasket)) .onErrorResumeNext(checkForError(continuation(code))); } Definition:
  37. onErrorResumeNext • Function • Recovery • On Failure

  38. Function public Func1<Throwable, Observable<Basket>> checkForError(final Func1<Data, Observable<Basket>> continuation) { return

    new Func1<Throwable, Observable<Basket>>() { @Override public Observable<Basket> call(Throwable error) { if (error instanceof BusinessRuleExeption) { return Observable.error(new NeedMoreDataException(continuation)); } return Observable.error(error); } }; }
  39. Function public Func1<Data, Observable<Basket>> continuation(final Barcode barcode) { return new

    Func1<Data, Observable<Basket>> { @Override public Observable<Basket> call (Data data){ return addToBasket(barcode, data); } } }
  40. Observer class BasketObserver implements Observer<Basket> { public onNext(Basket basket) {

    updateUI(basket); } public onError(Throwable error) { displayDialogFor(error).flatmap(withContinuationFrom(error)); } public onComplete() { } }
  41. Function private Observable<Data> displayDialogFor(Throwable error); private Func1<Data, Observable<Basket>> withContinuationFrom(NeedMoreData error)

    { return new Func1<Data, Observable<T>>() { @Override public Observable<Basket> call(final Data data) { return error.getContinuation().call(data); } }; }
  42. Dialog is reactive public class ReactiveDialog<T> extends DialogFragment { public

    Observable<T> show(final FragmentManager manager) { return Observable.create(new Observable.OnSubscribe<T>() { @Override public void call(rx.Subscriber<? super T> subscriber) { UUID key = subscriberVault.store(subscriber); getArguments().putSerializable(REACTIVE_DIALOG_KEY, key); show(manager, getClass().getSimpleName()); } }); } }
  43. Reactive Dialogs ?!

  44. Recap public Observable<Basket> addToBasket(Barcode code, Data data) { return addToBasketReq(code,

    data) .flatmap(checkAPISuccess()) .flatmap(addCustomerIfNeeded()) .map(applyDiffTo(cachedBasket)) .onErrorResumeNext(checkForError(continuation(code, data))); } Definition:
  45. None
  46. Resource RxAndroid collection of tools for RxJava on Android https://github.com/ReactiveX/RxAndroid

  47. Resource RxJava workshop a simple workshop to start http://goo.gl/eI3hMc

  48. We’re hiring ! join@novoda.com

  49. Questions ?

  50. @Dorvaryn +BenjaminAugustin-Dorvaryn Dorvaryn Benjamin Augustin Android Software Craftsman