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
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
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)
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
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.
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.
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
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
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
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/