Slide 1

Slide 1 text

What it means to be Reactive? RxJava for Android Constantine Mars Senior Android Developer @ DataArt GDG Dnipro and JUG Dnepr

Slide 2

Slide 2 text

–Ivan Morgillo, Alter Ego solutions, speech on DroidCon Berlin’2015 “A piece of cake, you know”

Slide 3

Slide 3 text

#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

Slide 4

Slide 4 text

Fundamentals The basic concepts behind Rx

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

#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:

Slide 7

Slide 7 text

#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

Slide 8

Slide 8 text

#dfua We will use it throughout presentation Lambda syntax t.setOnClickListener(v -> Timber.d("something")); .JAVA

Slide 9

Slide 9 text

RxJava. Observable & Observer Basics

Slide 10

Slide 10 text

#dfua It’s the source of events Observable rx.Observable observable = rx.Observable.create( new rx.Observable.OnSubscribe() { @Override public void call(Subscriber 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

Slide 11

Slide 11 text

#dfua It’s the source of events. It handles subscriptions through OnSubscribe callback Observable rx.Observable observable = rx.Observable.create( new rx.Observable.OnSubscribe() { @Override public void call(Subscriber 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

Slide 12

Slide 12 text

#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 observable = rx.Observable.create( new rx.Observable.OnSubscribe() { @Override public void call(Subscriber 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

Slide 13

Slide 13 text

#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 observable = rx.Observable.create( new rx.Observable.OnSubscribe() { @Override public void call(Subscriber 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

Slide 14

Slide 14 text

#dfua Observer rx.Observer observer = new rx.Observer() { @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.

Slide 15

Slide 15 text

#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 observer = new rx.Observer() { @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

Slide 16

Slide 16 text

#dfua But to make magic we need one more thing... Observer rx.Observer observer = new rx.Observer() { @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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

#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

Slide 19

Slide 19 text

#dfua Looks more readable, isn’t it? At least takes one screen, not three :) Observable + Observer with lambdas rx.Observable 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

Slide 20

Slide 20 text

#dfua Reactive Systems are: *Check ReactiveManifesto.org for detailed definitions: Message Driven Responsive Resilient Scalable (Elastic)

Slide 21

Slide 21 text

From everything, ...even from air :) Creating Observables

Slide 22

Slide 22 text

#dfua Observable.from() ENTER FILENAME/LANG ArrayList 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

Slide 23

Slide 23 text

#dfua Observable.just() ENTER FILENAME/LANG private List generate() { Random r = new Random(); int n = r.nextInt(5) + 5; ArrayList 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

Slide 24

Slide 24 text

#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

Slide 25

Slide 25 text

#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

Slide 26

Slide 26 text

#dfua RxBindings Button button; ... RxView.clicks(button) .map(v -> ++counter) .debounce(500, TimeUnit.MILLISECONDS) .subscribe(c -> display.show("button " + c)); .JAVA

Slide 27

Slide 27 text

Filtering, conditions RxJava Goods

Slide 28

Slide 28 text

#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.

Slide 29

Slide 29 text

#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.

Slide 30

Slide 30 text

#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.

Slide 31

Slide 31 text

#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.

Slide 32

Slide 32 text

#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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

#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()

Slide 35

Slide 35 text

#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.

Slide 36

Slide 36 text

#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

Slide 37

Slide 37 text

#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 :)

Slide 38

Slide 38 text

#dfua debounce long[] times = {3, 2, 1, 5, 2, 6}; rx.Observable> 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

Slide 39

Slide 39 text

#dfua sample long[] times = {3, 2, 1, 5, 4, 3, 1}; rx.Observable> 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

Slide 40

Slide 40 text

Merge and combine RxJava Goods

Slide 41

Slide 41 text

#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

Slide 42

Slide 42 text

#dfua zip rx.Observable first = Observable.range(0, 5); //int[] rx.Observable 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.

Slide 43

Slide 43 text

RxJava Error Handling

Slide 44

Slide 44 text

#dfua retry rx.Observable canFail = rx.Observable.create(new Observable.OnSubscribe() { @Override public void call(Subscriber 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

Slide 45

Slide 45 text

#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

Slide 46

Slide 46 text

#dfua retry In case of error we can check condition in retry() and then re- subscribe and try once more

Slide 47

Slide 47 text

Math and aggregate RxJava Goods

Slide 48

Slide 48 text

#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

Slide 49

Slide 49 text

#dfua Average rx.Observable integers = rx.Observable.range(0, 10); MathObservable.averageInteger(integers).subscribe(avg -> { getDisplay().show(avg); }); .JAVA Many methods of MathObservable has type-aware alternatives

Slide 50

Slide 50 text

#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

Slide 51

Slide 51 text

Ubiquitous mediums that like actors can play any role RxJava Subjects

Slide 52

Slide 52 text

#dfua Creating Subjects PublishSubject subject = PublishSubject.create(); example(subject); … ReplaySubject 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.

Slide 53

Slide 53 text

#dfua Example code private void example(Subject 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

Slide 54

Slide 54 text

#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

Slide 55

Slide 55 text

#dfua ReactiveX Diagrams In a hour of despair - seek inspiration at ReactiveX.io

Slide 56

Slide 56 text

RxJava Schedulers

Slide 57

Slide 57 text

#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

Slide 58

Slide 58 text

#dfua Schedulers rx.Observable source = rx.Observable.range(0, 10).map(integer -> { List 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

Slide 59

Slide 59 text

#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...

Slide 60

Slide 60 text

#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

Slide 61

Slide 61 text

Comes to play RxAndroid

Slide 62

Slide 62 text

#dfua Relationship to RxJava

Slide 63

Slide 63 text

#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

Slide 64

Slide 64 text

#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

Slide 65

Slide 65 text

RxJava Journey comes to end

Slide 66

Slide 66 text

#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/

Slide 67

Slide 67 text

#dfua ReactiveX RxJava Additional Reading

Slide 68

Slide 68 text

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