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

RxJava Basics

RxJava Basics

An introduction to ReactiveX focused on Java & Android examples. Covers basic code samples for RxJava and a set of use-cases for the pattern.

Cosmin Stefan-Dobrin

February 03, 2016
Tweet

Transcript

  1. RxJava & RxAndroid
    Bringing Reactive to the
    Android world

    View Slide

  2. Hi!
    google.com/+CosminStefan
    [email protected]
    http://silverbit.ro

    View Slide

  3. Contents
    Can I use ReactiveX in Java & Android?
    RxJava - how does it look like?
    What can I do with it in Android?
    What should I keep in mind?

    View Slide

  4. RxJava - What is it this about?
    Java implementation of Reactive Extensions
    Zero dependencies
    Relatively small footprint (<800KB)
    Stable (1.1.0 - December 2015)
    Support for Scala, Groovy, Clojure, Kotlin

    View Slide

  5. RxAndroid
    Android specific bindings for RxJava
    Driven by Jake Wharton
    Very thin layer above RxJava

    View Slide

  6. A quick detour

    View Slide

  7. Lambda Expressions
    Pass functionality as argument
    Short implementation of single-method anonymous classes
    Type inference
    (argumentList) -> { /* code */ }

    View Slide

  8. Lambda Expressions - Examples
    mTextView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    // perform action
    }
    });
    new Thread(new Runnable() {
    @Override
    public void run() {
    System.out.println("Hello World!");
    }
    });
    mTextView.setOnClickListener(v -> {
    // perform action
    });
    new Thread(() -> { System.out.println("Hello World!"); });

    View Slide

  9. Retrolambda
    Java 8 Lambda functionality down to Java 5
    Gradle integration for Android
    Editing support in Android Studio

    View Slide

  10. Let’s get to business

    View Slide

  11. RxJava - Starting simple
    Observable observable = Observable.just("Hello, GDG Bucharest!");

    View Slide

  12. RxJava - Starting simple (2)
    Observable observable = Observable.just("Hello, GDG Bucharest!");
    observable.subscribe(new Observer() {
    @Override
    public void onNext(String value) {
    System.out.println(value);
    }
    @Override
    public void onCompleted() {
    System.out.println("The End!");
    }
    @Override
    public void onError(Throwable e) {
    System.err.println("Ooops, I did it again!");
    }
    });

    View Slide

  13. RxJava - Making it simpler
    We can just specify a subset of the actions
    observable.subscribe(onNextAction, onErrorAction, onCompleteAction);
    observable.subscribe(onNextAction, onErrorAction);
    observable.subscribe(onNextAction);
    public interface Action1 {
    public void call(T value);
    }

    View Slide

  14. RxJava meets lambda
    Observable.just("Hello, GDG Bucharest!");

    View Slide

  15. RxJava meets lambda
    Observable.just("Hello, GDG Bucharest!")
    .subscribe(new Action1() {
    @Override
    public void call(String value) {
    System.out.println(value);
    }
    });

    View Slide

  16. RxJava meets lambda
    Observable.just("Hello, GDG Bucharest!")
    .subscribe(new Action1() {
    @Override
    public void call(String value) {
    System.out.println(value);
    }
    });
    Observable.just("Hello, GDG Bucharest!")
    .subscribe(s -> System.out.println(s))

    View Slide

  17. Transforming
    Observable.just("Hello, GDG Bucharest!")
    .map(new Func1() {
    @Override
    public Integer call(String s) {
    return s -> s.hashCode();
    }
    })
    .subscribe(new Action1() {
    @Override
    public void call(Integer hash) {
    System.out.println("The hash is: " + hash)
    }
    });

    View Slide

  18. Transforming
    Observable.just("Hello, GDG Bucharest!")
    .map(new Func1() {
    @Override
    public Integer call(String s) {
    return s -> s.hashCode();
    }
    })
    .subscribe(new Action1() {
    @Override
    public void call(Integer hash) {
    System.out.println("The hash is: " + hash)
    }
    });
    Observable.just("Hello, GDG Bucharest!")
    .map(s -> s.hashCode())
    .subscribe(hash -> System.out.println("The hash is: " + hash));

    View Slide

  19. Taking it to the next step
    Observable.just("Hello, GDG Bucharest!")
    .map(s -> s + ";Hello, World!")
    .flatMap(s -> {
    String[] pieces = s.split(";");
    return Observable.from(pieces);
    })
    .map(s -> s + "!!")
    .subscribe(s -> System.out.println(s));

    View Slide

  20. Taking it to the next step
    Observable.just("Hello, GDG Bucharest!")
    .map(s -> s + ";Hello, World!")
    .flatMap(s -> {
    String[] pieces = s.split(";");
    return Observable.from(pieces);
    })
    .map(s -> s + "!!")
    .subscribe(s -> System.out.println(s));
    Outputs:
    Hello, GDG Bucharest!!!
    Hello, World!!!

    View Slide

  21. RxJava - Creating a cold observable
    Observable.create(new Observable.OnSubscribe() {
    @Override
    public void call(Subscriber super String> subscriber) {
    // Do some more stuff
    subscriber.onNext("Hello, GDG Bucharest!");
    subscriber.onCompleted();
    }
    });

    View Slide

  22. RxJava - Creating a cold observable
    Observable.create(new Observable.OnSubscribe() {
    @Override
    public void call(Subscriber super String> subscriber) {
    // Do some more stuff
    subscriber.onNext("Hello, GDG Bucharest!");
    subscriber.onCompleted();
    }
    });
    Observable.create(subscriber -> {
    // Do some more stuff
    subscriber.onNext("Hello, GDG Bucharest!");
    subscriber.onCompleted();
    });

    View Slide

  23. Schedulers
    Mechanism to switch the threads on which steps get executed
    Schedulers.computation();
    Schedulers.io();
    Schedulers.immediate();
    AndroidSchedulers.mainThread();

    View Slide

  24. Schedulers - Power overwhelming
    public Observable loadImage(String url);

    View Slide

  25. Schedulers - Power overwhelming
    public Observable loadImage(String url);
    ...
    loadImage(testUrl)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(bitmap -> mImageView.setImageBitmap(bitmap));

    View Slide

  26. So, what does it do for me?

    View Slide

  27. Event Bus - the idea
    Decouple event emitters and consumers
    Steps:
    Subject + Subscriber
    Subject lives “forever”
    Anyone can post events
    Observers subscribe when needed

    View Slide

  28. Event Bus - How?
    Subjects to the rescue
    class RxBus {
    private final Subject mBus =
    new SerializedSubject<>(PublishSubject.create());
    public void post(MyEvent e) {
    mBus.onNext(e);
    }
    public Observable toObserverable() {
    return mBus.asObservable();
    }
    }

    View Slide

  29. Event Bus - How?
    Sending events:
    rxBus.post(new MyEvent());

    View Slide

  30. Event Bus - How?
    Sending events:
    rxBus.post(new MyEvent());
    Listening for events:
    rxBus.toObserverable()
    .subscribe(event -> {
    // do something with the event
    });

    View Slide

  31. Instant search throttling - the idea
    Wait until text has “stabilized”
    Steps:
    Observable + Debounce operator
    Watcher for text change events on InputTextView
    Conversion to observable events
    Debounce for limiting

    View Slide

  32. Instant search throttling - How?
    public Observable observableForTextViewChangeEvents(TextView tv);
    ...
    observableForTextViewChangeEvents(mTextView)
    // make sure it’s stable
    .debounce(400, TimeUnit.MILLISECONDS)
    // perform the search
    .map(textChangeEvent -> {
    return performSearch(textChangeEvent);
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    // and finally update the UI accordingly
    .subscribe(searchResults -> {
    updateUI(searchResults)
    });

    View Slide

  33. Load both from cache and from network
    Quickly show preliminary results from cache
    Steps:
    Concat / Merge operator
    Create observables for both types of load
    Merge the results and handle in common way
    Cache prefetching - the idea

    View Slide

  34. Cache prefetching - How?
    Observable cacheDataLoader = loadCacheData();
    Observable freshDataLoader = loadFreshData();

    View Slide

  35. Cache prefetching - How?
    Observable cacheDataLoader = loadCacheData();
    Observable freshDataLoader = loadFreshData();
    Observable.concat(cacheDataLoader, freshDataLoader)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe( data -> {
    // Update the UI with the available data
    mAdapter.setNewData(data);
    });

    View Slide

  36. Generic API / library - sync vs async
    Build a versatile API / library supporting both async or sync use
    Steps:
    Observable + subscribeOn()/observeOn()
    Expose Cold Observables (perform work upon subscription)
    Can be run either synchronously or asynchronously

    View Slide

  37. Generic API - sync vs async
    // Perform task on the current thread
    mMyFancyApi.doFancyTask()
    .subscribe(result -> doSomething());

    View Slide

  38. Generic API - sync vs async
    // Perform task on the current thread
    mMyFancyApi.doFancyTask()
    .subscribe(result -> doSomething());
    // Perform task asynchronously
    mMyFancyApi.doFancyTask()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(result -> doSomethingElse());

    View Slide

  39. Retrofit and RxJava, sitting in a tree
    Simplify network calls to APIs
    Steps:
    Retrofit + Operators + subscribeOn()/observeOn()
    Use the Rx-aware Retrofit calls
    Run network calls on Schedulers.io()
    Chain processing
    Switch back to main thread for UI updates

    View Slide

  40. Retrofit and RxJava, sitting in a tree
    mWebService.fetchPosts()
    // Split posts list to individual items
    .flatMap(posts -> Observable.from(posts))
    // Process each item
    .doOnNext(post -> storeToLocalDatabase(post))
    // Take just the last post
    .last()
    // Configure threads properly
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    // Handle final results
    .subscribe(post -> {
    updateUI(post);
    }, (error) -> {
    Log.e(TAG, "Network error!", error);
    }
    );

    View Slide

  41. What to keep in mind

    View Slide

  42. Android lifecycles
    Take Activity/Fragment lifecycle into account
    Care when outliving the bound components
    Unsubscribe when result not needed anymore
    RxLifecyle to the rescue
    “Lifecycle handling APIs for Android apps using RxJava”

    View Slide

  43. Parting thoughts
    Can certainly simplify certain scenarios
    Power comes from chaining operations
    Thread switching easier
    Synchronization still an issues
    Not a silver bullet
    Don’t overuse - performance can become an issue

    View Slide

  44. Rx enabled Projects
    https://github.com/square/retrofit - REST service wrapper
    https://github.com/jersey/jersey - REST service wrapper
    https://github.com/square/sqlbrite - Reactive database wrapper
    https://github.com/ReactiveX/RxNetty - Async network framework
    https://github.com/davidmoten/rtree - RTree implementation
    https://github.com/Netflix/Hystrix - Latency and fault tolerance library
    https://github.com/mulab/rx-realm - Mobile database system
    & many more....

    View Slide

  45. Thank you!
    google.com/+CosminStefan
    [email protected]
    http://silverbit.ro

    View Slide

  46. What’s next?
    http://reactivex.io/intro.html
    http://rxmarbles.com/
    http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
    http://www.slideshare.net/allegrotech/rxjava-introduction-context
    https://github.com/kaushikgopal/RxJava-Android-Samples
    https://github.com/evant/gradle-retrolambda
    https://github.com/JakeWharton/RxBinding

    View Slide