$30 off During Our Annual Pro Sale. View Details »

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. RxFy All The Things!
    No pain and easy refactoring

    View Slide

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

    View Slide

  3. View Slide

  4. Why ?

    View Slide

  5. Development is hell

    View Slide

  6. So what is RxJava ?

    View Slide

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

    View Slide

  8. What is an Observable ?

    View Slide

  9. What is an Observer ?

    View Slide

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

    View Slide

  11. Schematics

    View Slide

  12. map
    ● Function
    ● Transformation
    ● For each element

    View Slide

  13. flatMap
    ● Function
    ● Composition
    ● For each element

    View Slide

  14. onErrorResumeNext
    ● Function
    ● Recovery
    ● On Failure

    View Slide

  15. Async control
    ● subscribeOn(Scheduler) :
    ○ Execute observable on Scheduler
    ● observeOn(Scheduler) :
    ○ Deliver results to observer on Scheduler

    View Slide

  16. View Slide

  17. All is starting well
    ● addToBasket
    ○ Take a barcode
    ○ Return a basket

    View Slide

  18. Easy enough right ?
    private class AddToBasket extends AsyncTask {
    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);
    }
    }

    View Slide

  19. After 4 months...
    private class AddToBasket extends AsyncTask>> {
    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> doInBackground(Barcode... barcodes) {
    return AsyncResult.just(api.addToBasket(barcodes[0]));
    }
    @Override
    protected void onPostExecute(AsyncResult> response) {
    super.onPostExecute(response);
    new CheckForError(currentBasket, updater).execute(response);
    }
    }
    private class CheckForError extends AsyncTask>, Void, AsyncResult> {
    private final BasketViewUpdater updater;
    private final Basket currentBasket;
    CheckForError(Basket currentBasket, BasketViewUpdater updater) {
    this.currentBasket = currentBasket;
    this.updater = updater;
    }
    @Override
    protected AsyncResult doInBackground(AsyncResult>... responses) {
    AsyncResult> responseAsyncResult = responses[0];
    if(responseAsyncResult.isError()) {
    return AsyncResult.error(responseAsyncResult.error);
    }
    Response response = responseAsyncResult.value;
    if (response.isSuccess()){
    return AsyncResult.just(response.getValue());
    }
    return AsyncResult.error(new CustomError());
    }
    @Override
    protected void onPostExecute(AsyncResult basket) {
    super.onPostExecute(basket);
    new ApplyDiff(currentBasket, updater).execute(basket);
    }
    }
    private class ApplyDiff extends AsyncTask, Void, AsyncResult> {
    private final Basket currentBasket;
    private final BasketViewUpdater updater;

    View Slide

  20. So how to RxFy it ?

    View Slide

  21. Rx Style
    class BasketObserver implements Observer {
    public onNext(Basket basket) {
    updateUI(basket);
    }
    public onError(Throwable error) {
    displayDialogFor(error);
    }
    public onComplete() {
    }
    }

    View Slide

  22. Rx Style
    Definition:
    Usage:
    public Observable addToBasket(Barcode code);
    addToBasket(code)
    .subscribe(new BasketObserver());

    View Slide

  23. HTTP Standard ?

    View Slide

  24. Refactoring
    public Observable addToBasket(Barcode code) {
    return addToBasketReq(code).flatmap(checkAPISuccess());
    }
    private Observable> addToBasketReq(Barcode code);
    Definition:
    addToBasket(code)
    .subscribe(new BasketObserver());
    Usage:

    View Slide

  25. flatMap
    ● Function
    ● Composition
    ● For each element

    View Slide

  26. Function
    public Func1, Observable> checkApiSuccess() {
    return new Func1, Observable>() {
    @Override
    public Observable call(Response response) {
    if (response.isSuccess()) {
    return Observable.from(response.getBody());
    }
    return Observable.error(new CustomError(header));
    }
    };
    }

    View Slide

  27. Stateless ?

    View Slide

  28. Refactoring
    public Observable addToBasket(Barcode code) {
    return addToBasketReq(code)
    .flatmap(checkAPISuccess())
    .map(applyDiffTo(cachedBasket));
    }
    Definition:

    View Slide

  29. map
    ● Function
    ● Transformation
    ● For each element

    View Slide

  30. Function
    public Func1 applyDiffTo(final Basket basket) {
    return new Func1() {
    @Override
    public Basket call(Diff diff) {
    return basket.apply(diff);
    }
    };
    }

    View Slide

  31. Logic server side ?

    View Slide

  32. Refactoring
    public Observable addToBasket(Barcode code) {
    return addToBasketReq(code)
    .flatmap(checkAPISuccess())
    .flatmap(addCustomerIfNeeded())
    .map(applyDiffTo(cachedBasket));
    }
    Definition:

    View Slide

  33. Function
    private Func1> addCustomerIfNeeded() {
    return new Func1>() {
    @Override
    public Observable call(Diff diff) {
    if (diff.requireAddCustomer()) {
    return addCustomer(diff.getCustomerId());
    }
    return Observable.from(diff);
    }
    };
    }

    View Slide

  34. Oh what about the law ?

    View Slide

  35. UI in network flow ?

    View Slide

  36. Refactoring
    public Observable addToBasket(Barcode code) {
    return addToBasket(code, Data.emptyData);
    }
    public Observable addToBasket(Barcode code, Data data) {
    return addToBasketReq(code, data)
    .flatmap(checkAPISuccess())
    .flatmap(addCustomerIfNeeded())
    .map(applyDiffTo(cachedBasket))
    .onErrorResumeNext(checkForError(continuation(code)));
    }
    Definition:

    View Slide

  37. onErrorResumeNext
    ● Function
    ● Recovery
    ● On Failure

    View Slide

  38. Function
    public Func1> checkForError(final Func1Observable> continuation) {
    return new Func1>() {
    @Override
    public Observable call(Throwable error) {
    if (error instanceof BusinessRuleExeption) {
    return Observable.error(new NeedMoreDataException(continuation));
    }
    return Observable.error(error);
    }
    };
    }

    View Slide

  39. Function
    public Func1> continuation(final Barcode
    barcode) {
    return new Func1> {
    @Override
    public Observable call (Data data){
    return addToBasket(barcode, data);
    }
    }
    }

    View Slide

  40. Observer
    class BasketObserver implements Observer {
    public onNext(Basket basket) {
    updateUI(basket);
    }
    public onError(Throwable error) {
    displayDialogFor(error).flatmap(withContinuationFrom(error));
    }
    public onComplete() {
    }
    }

    View Slide

  41. Function
    private Observable displayDialogFor(Throwable error);
    private Func1> withContinuationFrom(NeedMoreData
    error) {
    return new Func1>() {
    @Override
    public Observable call(final Data data) {
    return error.getContinuation().call(data);
    }
    };
    }

    View Slide

  42. Dialog is reactive
    public class ReactiveDialog extends DialogFragment {
    public Observable show(final FragmentManager manager) {
    return Observable.create(new Observable.OnSubscribe() {
    @Override
    public void call(rx.Subscriber super T> subscriber) {
    UUID key = subscriberVault.store(subscriber);
    getArguments().putSerializable(REACTIVE_DIALOG_KEY, key);
    show(manager, getClass().getSimpleName());
    }
    });
    }
    }

    View Slide

  43. Reactive Dialogs ?!

    View Slide

  44. Recap
    public Observable addToBasket(Barcode code, Data data) {
    return addToBasketReq(code, data)
    .flatmap(checkAPISuccess())
    .flatmap(addCustomerIfNeeded())
    .map(applyDiffTo(cachedBasket))
    .onErrorResumeNext(checkForError(continuation(code, data)));
    }
    Definition:

    View Slide

  45. View Slide

  46. Resource
    RxAndroid
    collection of tools for RxJava on Android
    https://github.com/ReactiveX/RxAndroid

    View Slide

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

    View Slide

  48. We’re hiring !
    [email protected]

    View Slide

  49. Questions ?

    View Slide

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

    View Slide