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.

Benjamin AUGUSTIN

September 23, 2014
Tweet

More Decks by Benjamin AUGUSTIN

Other Decks in Programming

Transcript

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

    Iterable<T> getData() Async Future<T> getData() Observable<T> getData
  2. Async control • subscribeOn(Scheduler) : ◦ Execute observable on Scheduler

    • observeOn(Scheduler) : ◦ Deliver results to observer on Scheduler
  3. 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); } }
  4. 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;
  5. Rx Style class BasketObserver implements Observer<Basket> { public onNext(Basket basket)

    { updateUI(basket); } public onError(Throwable error) { displayDialogFor(error); } public onComplete() { } }
  6. 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:
  7. 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)); } }; }
  8. Function public Func1<Diff, Basket> applyDiffTo(final Basket basket) { return new

    Func1<Diff, Basket>() { @Override public Basket call(Diff diff) { return basket.apply(diff); } }; }
  9. 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); } }; }
  10. 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:
  11. 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); } }; }
  12. 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); } } }
  13. Observer class BasketObserver implements Observer<Basket> { public onNext(Basket basket) {

    updateUI(basket); } public onError(Throwable error) { displayDialogFor(error).flatmap(withContinuationFrom(error)); } public onComplete() { } }
  14. 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); } }; }
  15. 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()); } }); } }
  16. 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: