[Constantine Mars] RxJava for Android

[Constantine Mars] RxJava for Android

Presentation from GDG DevFest Ukraine 2015 - the biggest Google related event in the country. October 23-24, Lviv. Learn more at http://devfest.gdg.org.ua/

3a6de6bc902de7f75c0e753b3202ed52?s=128

Google Developers Group Lviv

October 23, 2015
Tweet

Transcript

  1. What it means to be Reactive? RxJava for Android Constantine

    Mars Senior Android Developer @ DataArt GDG Dnipro and JUG Dnepr
  2. –Ivan Morgillo, Alter Ego solutions, speech on DroidCon Berlin’2015 “A

    piece of cake, you know”
  3. #dfua Faces of the Reactive Programming World Ben Christensen, Netflix

    - RxJava Erik Meijer, Applied Duality - Rx.NET Jake Wharton, Square - RxAndroid Ivan Morgillo, Alter Ego - first book about RxAndroid
  4. Fundamentals The basic concepts behind Rx

  5. #dfua Time to meditate... Everything is a stream…

  6. #dfua The one who is listening is Observer And the

    other one, who is emitting events, is Subject, or Observable *Illustration from O’Reilly® HeadFirst “Design Patterns” book:
  7. #dfua This is well known interface in for both Android

    and Desktop developers Observer = Listener t.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Timber.d("something"); } }); .JAVA
  8. #dfua We will use it throughout presentation Lambda syntax t.setOnClickListener(v

    -> Timber.d("something")); .JAVA
  9. RxJava. Observable & Observer Basics

  10. #dfua It’s the source of events Observable rx.Observable<Integer> observable =

    rx.Observable.create( new rx.Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { for (int i = 0; i < N; i++) { Integer integer = random.nextInt(MAX); subscriber.onNext(integer); } subscriber.onCompleted(); } }); .JAVA Be aware: there is java.util.Observable besides rx.Observable - they’re not the same
  11. #dfua It’s the source of events. It handles subscriptions through

    OnSubscribe callback Observable rx.Observable<Integer> observable = rx.Observable.create( new rx.Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { for (int i = 0; i < N; i++) { Integer integer = random.nextInt(MAX); subscriber.onNext(integer); } subscriber.onCompleted(); } }); .JAVA Be aware: there is java.util.Observable besides rx.Observable - they’re not the same
  12. #dfua It’s the source of events. It handles subscriptions through

    OnSubscribe callback. When observer subscribes, it is named subscriber inside of a call() Observable rx.Observable<Integer> observable = rx.Observable.create( new rx.Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { for (int i = 0; i < N; i++) { Integer integer = random.nextInt(MAX); subscriber.onNext(integer); } subscriber.onCompleted(); } }); .JAVA Be aware: there is java.util.Observable besides rx.Observable - they’re not the same
  13. #dfua It’s the source of events. It handles subscriptions through

    OnSubscribe callback. When observer subscribes, it is named subscriber inside of a call() Observable rx.Observable<Integer> observable = rx.Observable.create( new rx.Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer> subscriber) { for (int i = 0; i < N; i++) { Integer integer = random.nextInt(MAX); subscriber.onNext(integer); } subscriber.onCompleted(); } }); .JAVA Be aware: there is java.util.Observable besides rx.Observable - they’re not the same
  14. #dfua Observer rx.Observer<Integer> observer = new rx.Observer<Integer>() { @Override public

    void onCompleted() { display.show("completed"); } @Override public void onError(Throwable e) { display.show("error: " + e.getMessage()); } @Override public void onNext(Integer integer) { display.show("next: " + integer); } }; observable.subscribe(observer); .JAVA It’s the bunch of Reactive callbacks that should be registered through subscription.
  15. #dfua It’s the bunch of Reactive callbacks that should be

    registered through subscription. And handles incoming onNext(), onCompleted() and onError() from Observable Observer rx.Observer<Integer> observer = new rx.Observer<Integer>() { @Override public void onCompleted() { display.show("completed"); } @Override public void onError(Throwable e) { display.show("error: " + e.getMessage()); } @Override public void onNext(Integer integer) { display.show("next: " + integer); } }; observable.subscribe(observer); .JAVA
  16. #dfua But to make magic we need one more thing...

    Observer rx.Observer<Integer> observer = new rx.Observer<Integer>() { @Override public void onCompleted() { display.show("completed"); } @Override public void onError(Throwable e) { display.show("error: " + e.getMessage()); } @Override public void onNext(Integer integer) { display.show("next: " + integer); } }; observable.subscribe(observer); .JAVA
  17. #dfua To subscribe. This means creating Subscription. Subscription Subscription subscription

    = observable.subscribe(observer); ... if(!subscription.isUnsubscribed()) { subscription.unsubscribe(); } .JAVA
  18. #dfua To subscribe. This means creating Subscription. In fact Subscription

    class is rarely used, but can be useful to unsubscribe when we don’t need to receive events Subscription Subscription subscription = observable.subscribe(observer); ... if(!subscription.isUnsubscribed()) { subscription.unsubscribe(); } .JAVA
  19. #dfua Looks more readable, isn’t it? At least takes one

    screen, not three :) Observable + Observer with lambdas rx.Observable<Integer> observable = rx.Observable.create(subscriber -> { for (int i = 0; i < N; i++) subscriber.onNext(random.nextInt(MAX)); subscriber.onCompleted(); }); observable.subscribe( integer -> display.show("next: " + integer), throwable -> display.show("error: " + throwable.getMessage()), () -> display.show("completed")); .JAVA
  20. #dfua Reactive Systems are: *Check ReactiveManifesto.org for detailed definitions: Message

    Driven Responsive Resilient Scalable (Elastic)
  21. From everything, ...even from air :) Creating Observables

  22. #dfua Observable.from() ENTER FILENAME/LANG ArrayList<Integer> arrayList = new ArrayList<>(); int

    MAX_N = random.nextInt(12) + 5; for (int i = 0; i < MAX_N; i++) arrayList.add(random.nextInt(MAX)); rx.Observable.from(arrayList) .subscribe( integer -> display.show("next: " + integer), throwable -> display.show("error: " + throwable.getMessage()), () -> display.show("complete")); .JAVA
  23. #dfua Observable.just() ENTER FILENAME/LANG private List<Integer> generate() { Random r

    = new Random(); int n = r.nextInt(5) + 5; ArrayList<Integer> a = new ArrayList<>(); for (int i = 0; i < n; i++) a.add(r.nextInt(100)); return a; } public void just() { rx.Observable.just(generate()) .subscribe(integer -> display.show("next: " + integer), throwable -> display.show("error: " + throwable.getMessage()), () -> display.show("complete")); } .JAVA
  24. #dfua Observable.interval() Random r = new Random(); rx.Observable.interval(2, TimeUnit.SECONDS) .map(t

    -> new long[]{t, r.nextInt(100)}) .limit(5) .subscribe( tuple -> display.show("next: " + Arrays.toString(tuple)), throwable -> { }, () -> display.show("complete")); .JAVA
  25. #dfua Retrofit RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build(); GitHubService

    service = restAdapter.create(GitHubService.class); // Retrofit can return observable which we handle as any other observable service.listRepos("c-mars") .flatMap(Observable::from) .limit(10) .subscribe(repo -> display.show("next: " + repo.toString()), throwable -> display.show("error: " + throwable.getMessage()), () -> display.show("completed")); .JAVA
  26. #dfua RxBindings Button button; ... RxView.clicks(button) .map(v -> ++counter) .debounce(500,

    TimeUnit.MILLISECONDS) .subscribe(c -> display.show("button " + c)); .JAVA
  27. Filtering, conditions RxJava Goods

  28. #dfua Blocking private final AmmeterReadings[] data = { new AmmeterReadings(1,

    0.5), ... }; private static float getMaxValue(AmmeterReadings[] data) { return MathObservable.max(rx.Observable.from(data) .map(AmmeterReadings::getCurrent)) .toBlocking().firstOrDefault(0L); } .JAVA By default rx.Observable is async. But it can be converted to BlockingObservable and return result in-place, using functional computations.
  29. #dfua first, last, take, orDefault private final AmmeterReadings[] data =

    { new AmmeterReadings(1, 0.5), ... }; private static float getMaxValue(AmmeterReadings[] data) { return MathObservable.max(rx.Observable.from(data) .map(AmmeterReadings::getCurrent)) .toBlocking().firstOrDefault(0L); } .JAVA We can take first, last or any item from BlockingObservable. If it’s empty, we can define default value.
  30. #dfua take from Observable private final AmmeterReadings[] data = {

    new AmmeterReadings(1, 0.5), ... }; private static float getMaxValue(AmmeterReadings[] data) { return MathObservable.max( rx.Observable.from(data) .map(AmmeterReadings::getCurrent) .takeLast(5) ) .toBlocking().firstOrDefault(0L); } .JAVA rx.Observable (non-blocking) provides method take() to take multiple items from stream.
  31. #dfua singleOrDefault rx.Observable.range(0, 0) .singleOrDefault(-1) .forEach(i -> getDisplay().show("single:" + i));

    .JAVA Check whether rx.Observable contains only one event/item.
  32. #dfua defaultIfEmpty rx.Observable.range(0, max) .defaultIfEmpty(999) .forEach(i -> getDisplay().show("range 0->" +

    max + ", value (999 if empty):" + String.valueOf(i))); .JAVA Almost the same as singleOrDefault
  33. #dfua toIterable final Integer[] data = {200, 4, 145, -1,

    10, -12, 80}; Iterable<Integer> iterable = rx.Observable.from(data) .toBlocking().toIterable(); for (Integer i : iterable) { display.show("iterable:" + i.toString()); } .JAVA BlockingObservable.toIterable() converts rx.Observable to collection
  34. #dfua forEach final Integer[] data = {200, 4, 145, -1,

    10, -12, 80}; rx.Observable.from(data) .forEach(i -> display.show("iterable:" + i.toString())); .JAVA forEach() is just shorcut to .subscribe()
  35. #dfua takeUntil rx.Observable.range(0, 10) .takeUntil(i -> i == 5) .forEach(i

    -> getDisplay().show(String.valueOf(i))); // out: 0, 1, 2, 3, 4, 5 .JAVA Just a variant of take(), which completes when condition matches.
  36. #dfua contains rx.Observable.range(0, max) .contains(2) .forEach(i -> getDisplay().show("range: " +

    0 + "->" + max + ",contains 2: " + String.valueOf(i))); // out: true (or false) .JAVA Check whether stream contains certain value
  37. #dfua filter rx.Observable.range(0, 10) .filter(i -> i > 5 &&

    i < 9 ) .forEach(i -> getDisplay().show(i)); // out: 6, 7, 8 .JAVA Like takeUntil, just filter :)
  38. #dfua debounce long[] times = {3, 2, 1, 5, 2,

    6}; rx.Observable<Pair<Integer, Long>> observable = rx.Observable.create(subscriber -> { int sz = times.length; for (int i = 0; i < sz; i++) { try { long t = times[i]; TimeUnit.MILLISECONDS.sleep(t); subscriber.onNext(new Pair<>(i, t)); } catch (InterruptedException e) { subscriber.onError(e); } } subscriber.onCompleted(); }); observable.debounce(4, TimeUnit.MILLISECONDS) .subscribe(pair -> getDisplay().show("out: value=" + pair.first + ", time=" + pair.second)); .JAVA
  39. #dfua sample long[] times = {3, 2, 1, 5, 4,

    3, 1}; rx.Observable<Pair<Integer, Long>> observable = rx.Observable.create(subscriber -> { int sz = times.length; for (int i = 0; i < sz; i++) { try { long t = times[i]; TimeUnit.MILLISECONDS.sleep(t * 10); subscriber.onNext(new Pair<>(i, t)); } catch (InterruptedException e) { subscriber.onError(e); } } subscriber.onCompleted(); }); observable.sample(40, TimeUnit.MILLISECONDS) .subscribe(pair -> getDisplay().show("out: value=" + pair.first + "; time=" + pair.second)); .JAVA
  40. Merge and combine RxJava Goods

  41. #dfua merge rx.Observable first = Observable.range(0, 5); //int[] rx.Observable second

    = Observable.from(new String[]{"one", "two", "three", "four", "five"}); //String[] rx.Observable.merge(first, second) .forEach(item -> getDisplay().show(item.toString())); .JAVA Merges items from separate rx.Observables to single stream
  42. #dfua zip rx.Observable<Integer> first = Observable.range(0, 5); //int[] rx.Observable<String> second

    = Observable.from(new String[]{"one", "two", "three", "four", "five"}); //String[] rx.Observable.zip(first, second, (i, s) -> new Pair(s, i)) .forEach(pair -> getDisplay().show(pair.toString())); .JAVA Merges items from separate rx.Observables to single stream using combining function.
  43. RxJava Error Handling

  44. #dfua retry rx.Observable<Integer> canFail = rx.Observable.create(new Observable.OnSubscribe<Integer>() { @Override public

    void call(Subscriber<? super Integer> subscriber) { for (int i = 0; i < 6; i++) { switch (i) { case 3: if (!failedOnce) { failedOnce = true; subscriber.onError(new Error()); return; } break; case 5: subscriber.onError(new Throwable()); return; } subscriber.onNext(i); } subscriber.onCompleted(); } }); .JAVA
  45. #dfua retry canFail.retry((integer, throwable) -> { boolean retry = (throwable

    instanceof Error); getDisplay().show("retry, errors: " + integer); return retry; }) .subscribe(i -> { getDisplay().show(i); }, throwable -> { getDisplay().show("error: " + throwable.getMessage()); }); .JAVA
  46. #dfua retry In case of error we can check condition

    in retry() and then re- subscribe and try once more
  47. Math and aggregate RxJava Goods

  48. #dfua MathObservable private final AmmeterReadings[] data = { new AmmeterReadings(1,

    0.5), ... }; private static float getMaxValue(AmmeterReadings[] data) { return MathObservable.max(rx.Observable.from(data) .map(AmmeterReadings::getCurrent)) .toBlocking().firstOrDefault(0L); } .JAVA Plugin MathObservable: compile 'io.reactivex:rxjava-math:1.0.0' max, average, sum, count
  49. #dfua Average rx.Observable<Integer> integers = rx.Observable.range(0, 10); MathObservable.averageInteger(integers).subscribe(avg -> {

    getDisplay().show(avg); }); .JAVA Many methods of MathObservable has type-aware alternatives
  50. #dfua reduce rx.Observable.range(0, 10).reduce((a, b) -> { int c =

    a + b; getDisplay().show("reduce: a=" + a + " + " + b + " = " + c); return c; }).forEach(value -> getDisplay().show("result: " + value)); .JAVA Classic reduce operation, common for all functional programming languages
  51. Ubiquitous mediums that like actors can play any role RxJava

    Subjects
  52. #dfua Creating Subjects PublishSubject<String> subject = PublishSubject.create(); example(subject); … ReplaySubject<String>

    subject = ReplaySubject.createWithSize(2); example(subject); .JAVA Subjects have method .create() for this. ReplaySubject can also be created with predefined number of events to replay on subscription.
  53. #dfua Example code private void example(Subject<String, String> subject) { subject.onNext("before

    1"); subject.onNext("before 2"); subject.onNext("before 3"); subject.onNext("before 4"); subject.subscribe(s -> getDisplay().show("subscribed: " + s)); subject.onNext("after 5"); subject.onNext("after 6"); subject.onNext("after 7"); subject.onNext("after 8"); subject.onCompleted(); } .JAVA Subject can act both like Observable and Observer. So we can call .onNext, . onComplete manually and trigger subscription callbacks
  54. #dfua Subjects behaviour PublishSubject Is just a proxy for events

    AsyncSubject Replays only last event onComplete ReplaySubject Replays last N events and then proxies the same as Publish BehaviorSubject Replays only last event and then proxies the same as Publish
  55. #dfua ReactiveX Diagrams In a hour of despair - seek

    inspiration at ReactiveX.io
  56. RxJava Schedulers

  57. #dfua Schedulers rx.Observable.from(readFromFile(context)) .subscribeOn(Schedulers.io()) .forEach(line -> textView.append("\n" + line)); .JAVA

    By default rx.Observable is single-threaded. Here come Schedulers to hide threading and synchronization behind the functional interface. Just call .subscribeOn() and define which kind of threading you want
  58. #dfua Schedulers rx.Observable source = rx.Observable.range(0, 10).map(integer -> { List<Integer>

    outs = new ArrayList<>(); for (int i = 0; i < 1000; i++) { for (int j = 0; j < 1000; j++) { outs.add((int) Math.pow(i, j)); } } return outs; }).flatMap(Observable::from); MathObservable.sumInteger(source) .subscribeOn(Schedulers.computation()) .subscribe(integer -> textView.setText("final sum: " + integer.toString())); .JAVA
  59. #dfua Android UI MathObservable.sumInteger(source) .subscribeOn(Schedulers.computation()) .subscribe(integer -> textView.setText("final sum: "

    + integer.toString())); .JAVA Scheduler move execution to another appropriate thread. But when we try to update UI from this chain - something bad happens...
  60. #dfua Android UI MathObservable.sumInteger(source) .subscribeOn(Schedulers.computation()) .subscribe(integer -> textView.setText("final sum: "

    + integer.toString())); .JAVA Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. E/AndroidRuntime: at android.view.ViewRootImpl.checkThread(ViewRootImpl.java: 6556) E/AndroidRuntime: at android.view.ViewRootImpl.invalidateChildInParent (ViewRootImpl.java:942) E/AndroidRuntime: at android.view.ViewGroup.invalidateChild(ViewGroup.java:5081) E/AndroidRuntime: at android.view.View.invalidateInternal(View.java:12713) E/AndroidRuntime: at android.view.View.invalidate(View.java:12677) E/AndroidRuntime: at android.view.View.invalidate(View.java:12661) ... CONSOLE
  61. Comes to play RxAndroid

  62. #dfua Relationship to RxJava

  63. #dfua AndroidSchedulers rx.Observable.from(readFromFile(context)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .forEach(line -> textView.append("\n" + line));

    .JAVA .subscribeOn defines thread on which computations run, .observeOn defines thread on which rx.Observer callbacks will run
  64. #dfua Activity Lifecycle // Activity private Subscription subscription; protected void

    onCreate(Bundle savedInstanceState) { this.subscription = observable.subscribe(this); } ... protected void onDestroy() { this.subscription.unsubscribe(); super.onDestroy(); } .JAVA Use subscription or CompositeSubscription
  65. RxJava Journey comes to end

  66. #dfua Where to look? • JavaDoc: http://reactivex.io/RxJava/javadoc/rx/Observable.html • List of

    Additional Reading from RxJava Wiki: https://github. com/ReactiveX/RxJava/wiki/Additional-Reading • RxJava Essentials by Ivan Morgillo: https://www.packtpub. com/application-development/rxjava-essentials • RxMarbles - interactive diagrams: http://rxmarbles.com/
  67. #dfua ReactiveX RxJava Additional Reading

  68. Thank you! Questions? +ConstantineMars dataart.com.ua dnipro.gdg.org.ua