2. Introduction
1. Developing a complex Android app that has lots of
network connections, user interactions, and animations
often means writing code that is full of nested
callbacks.
2. Sometimes called callback hell, is not only lengthy and
hard to understand, hard to maintain.
3. ReactiveX offers an alternative approach that is both
clear and concise, to manage asynchronous tasks and
events.
3. Overview
1. What is ReactiveX?
2. Observables, Observers
3. Operator
4. Schedulers
5. Retrofit with RxAndroid
4. What is ReactiveX?
ReactiveX is a library for composing
asynchronous and event-based programs by using
observable sequences.
RxJava, which is a port of the Reactive Extensions
library from .NET, enables Android apps to be
built in this style.
5. Async programming
Nowadays programming in an imperative single threaded way
usually leads to strange behaviors, blocking non responsive
UIs and therefore a bad user experience.
This can be avoided by handling unpredicted things
asynchronously. For example actively waiting for a database
query or a webservice call can cause an application freeze, if
the network is not responsive.
6. Async programming
public List<Todo> getTodos() {
List<Todo> todosFromWeb
= // query a webservice (with bad
network latency)
return todosFromDb;
}
public void
getTodos(Consumer<List<Todo>>
todosConsumer) {
Thread thread = new Thread(()-> {
List<Todo> todosFromWeb = //
query a webservice
todosConsumer.accept(todosFromWeb);
});
thread.start();
}
8. Observables, Observers and
Subscriptions
1. Observables
Emit items (Objects, Strings, Integers etc.).
Does not start emitting till someone subscribes.
2. Observers
To watch Observables by subscribing to them and consumes data
3. Manipulation operators (this is the “functional programming” part)
Transforms
Filters
Etc.
The observable we just created will emit its data only when it has at least one
observer.
Similar to the observer pattern. Except, doesn’t start emitting till there is a subscriber.
9. Creating Observable
RxJava provides several methods to create an
observable.
Observable.just() - Allows to create an observable as
wrapper around other data types
Observable.from() - takes a collection or an array and
emits their values in their order in the data structure
Observable.fromCallable() - Allows to create an
observable for a Callable`
To create observers:
Implement Action1 - Allow you to create a simple observer
which has a call methods. This method is called if a new
object is emitted.
10. Defining Observables
// Observables emit any number of items to be processed
// The type of the item to be processed needs to be specified as a "generic type"
// In this case, the item type is `String`
Observable<String> myObservable = Observable.create(
new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> sub) {
// "Emit" any data to the subscriber
sub.onNext("a");
sub.onNext("b");
sub.onNext("c");
// Trigger the completion of the event
sub.onCompleted();
}
}
);
This observable event emits the data “a”, “b”, “c” and then completes.
11. Defining Observers
Observer<String> mySubscriber = new Observer<String>() {
// Triggered for each emitted value
@Override
public void onNext(String s) { System.out.println("onNext: " + s); }
// Triggered once the observable is complete
@Override
public void onCompleted() { System.out.println("done!"); }
// Triggered if there is any errors during the event
@Override
public void onError(Throwable e) { }
};
Now let’s create a Observer to consume this emitted data from the
Observable:
12. Subscribing to Observables
An Observer can be attached to an Observable in order to
respond to emitted data with:
• // Attaches the subscriber above to the observable object
• myObservable.subscribe(mySubscriber);
Outputs:
// onNext: "a"
// onNext: "b"
// onNext: "c"
// done!
Observable Observers
13. Subscribing to Observables
void onNext(T t):
Provides the Observer with a new item to observe.
may call this method 0 or more times.
will not call this method again after it calls either onCompleted or
onError
void onCompleted():
Notifies the Observer that the Observable has finished sending push-based
notifications.
void onError(Throwable e):
Notifies the Observer that the Observable has experienced an error
condition.
Subscription subscribe(final Observer<? super T> observer):
Subscribes to an Observable and provides an Observer that implements
functions to handle the items the Observable emits and any error or
completion
14. Operator
Just — convert an object or a set of objects into an Observable that
emits that or those objects
Observable.just(1, 2, 3).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
System.out.println("Complete!");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
System.out.println("onNext!");
}
});
System.out: onNext!: 1
System.out: onNext!: 2
System.out: onNext!: 3
System.out: Complete!
15. Operator
From — Converts an Array into an Observable that emits the items
in the Array.
Integer[] items = {0, 1, 2, 3, 4};
Observable.from(items).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
System.out.println("Complete!");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
System.out.println("onNext from!" + integer);
}
});
System.out: onNext!0
System.out: onNext!1
System.out: onNext!2
System.out: onNext!3
System.out: onNext!4
System.out: Complete!
16. Operator
Filter : Filters items emitted by an Observable by only emitting
those that satisfy a specified predicate.
Observable.just(1, 2, 3, 4, 5)
.filter(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer integer) {
return integer < 4;
}
}).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
System.out.println("Complete!");
}
@Override
public void onError(Throwable e) {
System.out.println("onError!");
}
@Override
public void onNext(Integer integer) {
System.out.println("onNext just!" + integer);
}
});
System.out: onNext!0
System.out: onNext!1
System.out: onNext!2
System.out: onNext!3
System.out: Complete!
17. Operator
Map: Transform the items emitted by an Observable by applying a
function to each item
Observable.just(1, 2, 3, 4, 5)
.map(new Func1<Integer, Integer>() {
@Override
public Integer call(Integer integer) {
return integer * 10;
}
}).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
System.out.println("Complete!");
}
@Override
public void onError(Throwable e) {
System.out.println("onError!");
}
@Override
public void onNext(Integer integer) {
System.out.println("onNext!" + integer);
}
});
System.out: onNext! 10
System.out: onNext! 20
System.out: onNext! 30
System.out: onNext! 40
System.out: onNext! 50
System.out: Complete!
19. Schedulers
RxJava is synchronous by default, but work can be defined
asynchronously using schedulers. For instance, we can define that
the network call should be done on a background thread, but the
callback should be done on the main UI thread.
Observable.from(doLongNetwork())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getObserver())
1. Observable
2.Schedulers
3. Observer
20. Schedulers
These schedulers than then be used to control which thread an observable or
the subscriber are operating on using the subscribeOn() and observeOn()
21. Schedulers
subscribeOn():
Basic rules of rxjava threading:
1. rxjava is single threaded by default
2. subscribeOn only affects upstream
3. only the subscribeOn closest to the source matters
4. observeOn only affects downstream
22. Schedulers
1. rxjava is single threaded by default
When you do not use observeOn, subscribeOn, or an operator that runs on a particular
scheduler , the callback will be receieved on the thread subscribe happened on.
2. subscribeOn only affects upstream
getANumberObservable()
.subscribeOn(Schedulers.io())
.map(new Func1<Integer, String>() {
@Override
public String call(Integer integer) {
Log.i("Operator thread", Thread.currentThread().getName());
return String.valueOf(integer);
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.i("Subscriber thread", Thread.currentThread().getName() + " onNext: " +
s);
}
});
Output:
Observable thread: RxIoScheduler-2
Operator thread: RxIoScheduler-2
Subscriber thread: RxIoScheduler-2
onNext: 1
23. Schedulers
3. Only the subscribeOn closest to the source matters:
Observable.just("Some String")
.map(new Func1<String, Integer>() {
@Override
public Integer call(String s) {
return s.length();
}
})
.subscribeOn(Schedulers.computation()) // changing to computation
.subscribeOn(Schedulers.io()) // won’t change the thread to IO
.subscribe(new Observer<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
System.out.println("onNext!" + integer);
Log.d("onNext", "Number " + integer + " Thread: " + Thread.currentThread().getName());
}
});
Output
System.out: onNext!11 onNext:
Number 11
Thread: RxComputationScheduler-1
24. Schedulers
4. observeOn only affects downstream
ObserveOn function () pass an argument is a Scheduler will make the
Operator and Subscriber called behind it is running on thread
provided by this Scheduler.
26. Deferring Observable
Create(…): actually creates Observable immediately.
public final static <T> Observable<T> create(OnSubscribe<T> f) {
return new Observable<T>(hook.onCreate(f));
}
Defer(…): creates Observable only when subscriber subscribes, create a
new Observable each time you get a subscriber.
public final static <T> Observable<T> defer(Func0<Observable<T>>
observableFactory) {
return create(new OnSubscribeDefer<T>(observableFactory));
}
27. Deferring Observable
SomeType instance = new SomeType();
Observable<String> value = instance.valueObservable();
instance.setValue("Some Value");
value.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
Log.d("Thinhdt", "Defer Observable: " + s);
}
});
Output:
Thinhdt: Defer Observable: null
28. Deferring Observable : Solution
public Observable<String> valueObservable() {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext(value);
subscriber.onCompleted(); }
});
}
Observable.cr
eate()
public Observable<String> valueObservable() {
return Observable.defer(new Func0<Observable<String>>() {
@Override
public Observable<String> call() {
return Observable.just(value);
}
});
}
Observable.
defer
29. Replacing AsyncTask with Observables
// This constructs an `Observable` to download the image
public Observable<Bitmap> getImageNetworkCall() {
// Insert network call here!
}
// Construct the observable and use `subscribeOn` and `observeOn`
// This controls which threads are used for processing and observing
Subscription subscription = getImageNetworkCall()
// Specify the `Scheduler` on which an Observable will operate
.subscribeOn(Schedulers.io())
// Specify the `Scheduler` on which a subscriber will observe this `Observable`
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Bitmap>() {
// This replaces `onPostExecute(Bitmap bitmap)`
@Override
public void onNext(Bitmap bitmap) {
// Handle result of network request
}
@Override
public void onCompleted() {
// Update user interface if needed
}
@Override
public void onError() {
// Update user interface to handle error
}
});
1. Thread to perform the task in the
background used subscribeOn()
2. To update UI on main thread used
observeOn()
30. Retrofit with RxAndroid
1. public interface MyApiEndpointInterface {
@GET("/users/{username}")
Observable<User> getUser(@Path("username") String
username);
}
2. MyApiEndpointInterface apiService =
retrofit.create(MyApiEndpointInterface.class);
// Get the observable User object
Observable<User> call = apiService.getUser(username);
// To define where the work is done, we can use `observeOn()` with Retrofit
// This means the result is handed to the subscriber on the main thread
call.observeOn(AndroidSchedulers.mainThread()).subscribe(new
Subscriber<User>() {
@Override
public void onNext(User user) {
// Called once the `User` object is available
}
@Override
public void onCompleted() {
// Nothing to do here
}
@Override
public void onError(Throwable e) {
// cast to retrofit.HttpException to get the response code
if (e instanceof HttpException) {
HttpException response;
int code = response.code();
}
}
});
This observable event emits the data “a”, “b”, “c” and then completes.
These schedulers than then be used to control which thread an observable or the subscriber are operating on using the subscribeOn() and observeOn()
subscribeOn() instructs the source Observable which thread to emit items on
It is helpful to instruct a source Observable which Scheduler to use via subscribeOn(), and the source Observable will emit items on one of that Scheduler's threads
Bạn cần lưu ý điều này khi sử dụng các hàm như Observable.just(), Observable.from() hay Observable.range(): Những hàm này sẽ nhận vào giá trị ngay khi chúng được khởi tạo nên subscribeOn() sẽ không có tác dụng; Nguyên nhân là do subscribeOn() chỉ có tác dụng khi hàm subscribe() được gọi đến, mà những hàm khởi tạo nói trên lại khởi tạo Observable trước khi gọi subscriber() nên các bạn cần tránh đưa vào các giá trị mà cần tính toán trong 1 khoảng thời gian dài (blocking) vào các hàm khởi tạo đó.
Thay vào đó đối với các hàm blocking thì bạn có thể sử dụng Observable.create() hoặc Observable.defer(). 2 hàm này về cơ bản sẽ đảm bảo là Observable sẽ chỉ được khởi tạo khi hàm subscribe() được gọi đến.
The only downside to defer() is that it creates a new Observable each time you get a subscriber. create() can use the same function for each subscriber, so it's more efficient. As always, measure performance and optimize if necessary.