“Unless you can model the entire system of your
app in a synchronous fashion, having a single
asynchronous source will ultimately break the
traditional imperative style of programming we’re
used to.”
Advantages
- Simplifies the ability to chain async operations.
- Surfaces errors sooner.
- Easy threading
- Helps reduce the need for state variables that can introduce bugs.
What is RxJava?
RxJava is Reactive Extensions for the JVM - a library for
composing asynchronous and event-based programs using
Observable sequences for the Java VM
What is RxJava?
In other words we are pushing data instead of pulling it
without worrying about threads.
On top of that you are given an amazing toolbox of
functions to combine create and filter the streams
Should I use RxJava 2 instead of 1?
YES!
- Mar 2018 - end of RxJava 1 support
- Better performance
- Lower memory consumption
- Can’t use null
- Backpressure (Flowable)
Iterable vs Observable, seems familiar?
Event Iterable Observable
retrieve data T next() onNext(T)
discover error throw Exception onError(Exception)
complete !hasNext() onCompleted()
transform fromIterable() toList()
Observables and Observers
The basic building blocks of reactive code are Observables
(Producer) and Observers (Consumer).
The Observable object is the one who does the job.
An Observer consumes the items emitted by the Observable.
An Observable calls Observer#onNext() for every item, followed
by either Observer#onComplete() or Observer#onError().
Observables and Friends
- Flowable: 0..N flows, supporting reactive streams and backpressure
- Observable: 0..N flows, no backpressure
- Single: a flow of exactly 1 item or am error
- Completable: a flow without items but only a completion or error signal
- Maybe: a flow with no items, exactly one item or an error
Disposables
Disposables represents the link between your Observable and
your Subscriber
Disposable d = Observable.timer(0, TimeUnit.SECONDS).subscribe(aLong -> {});
//Adding the disposable to CompositeDisposable
disposables.add(d);
//You can remove a disposable using delete method
disposables.delete(d);
//The container can be later reused
disposables.clear();
//The container cannot be reused
disposables.dispose();
Creating Observable from async (The ugly
way)
Observable<Movie> movieObservable = Observable.create(
new ObservableOnSubscribe<Todo>() {
@Override
public void subscribe(ObservableEmitter<Movie> emitter)
throws
Exception {
try {
// Fetches Movie objects from the network, database, etc.
List<Movie> movies = getMovies();
for (Movie movie : movies) {
emitter.onNext(movie);
}
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
});
When we want to create an Observable using async code
Creating Observable using lambda
Observable<Movie> movieObservable = Observable.create(emitter -> {
try {
// Fetches Movie objects from the network, database, etc.
List<Movie> movies = getMovies();
for (Movie movie : movies) {
emitter.onNext(movie);
}
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
Lambda expressions is only a “new” way of doing the same thing we
always been able to do but in a cleaner and less wordy the new way of on
how to use anonymous inner classes.
Defining Observers
Let's take the most basic example to understand how
this is structured.
movieObservable.subscribe(new Observer<String>() {
@Override
public void onNext(String s) { System.out.println("onNext: " + s); }
@Override
public void onCompleted() { System.out.println("done!"); }
@Override
public void onError(Throwable e) { }
});
public interface Observer<T> {
void onNext(T t);
void onCompleted();
void onError(Throwable e);
}
Operators
There is 248 operators we will focus on the common
- map()
- flatmap()
- filter()
- distinct()
- take()
- count()
Operators - Map - Example
Observable.just("Hello!")
.map(new Func1<String, String>() {
@Override
public String call(String s) {
return s + " Dan";
}
})
.subscribe(s -> System.out.println(s));
Observable.just("Hello!")
.map(s -> s + " Dan")
.subscribe(s -> System.out.println(s));
Operators - Flatmap
- Transforms the items emitted by an Observable
into Observables, then flatten the emissions from
those into a single Observable (no order)
Flatmap vs Map
- Map returns an object of type T while FlatMap returns an Observable<T>.
- FlatMap - It basically merges an observable sequence of observable
sequences into a single observable sequence.
Operators - Filter
- Emits the same items it received, only if it passes
the boolean check (predicate)
Operators - Filter - Example
Observable.just("Hello!")
.flatMap(urls -> Observable.from(urls))
.flatMap(url -> getTitle(url))
.filter(title -> title != null)
.subscribe(url -> System.out.println(url));
Operators - Reduce
- The Distinct operator filters an Observable by only
allowing items through that have not already been
emitted.
Error handling
- onError() is called if an exception is thrown
at any time.
- The operators do not have to handle the
exception
public interface Observer<T> {
void onNext(T t);
void onCompleted();
void onError(Throwable e);
}
Multiple REST Calls
Observable<JsonObject> userObservable = repo
.create(User.class)
.getUser(loginName)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Observable<JsonArray> eventsObservable = repo
.create(Event.class)
.listEvents(loginName)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Retrofit repo = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
Let’s first setup two observables for the two network requests
below:
Zip magic
Observable<UserAndEvents> combined = Observable.zip(userObservable,
eventsObservable, new Func2<JsonObject, JsonArray, UserAndEvents>() {
@Override
public UserAndEvents call(JsonObject jsonObject, JsonArray jsonElements) {
return new UserAndEvents(jsonObject, jsonElements);
}
});
We use RxJava’s zip method to combine
our two Observables and wait for them to
complete before creating a new Observable.
Login case
// force-disable the button
submitButton.setEnabled(false);
emailChangeObservable = RxTextView.textChangeEvents(email);
passwordChangeObservable = RxTextView.textChangeEvents(password);
Observable.combineLatest(emailChangeObservable, passwordChangeObservable,
(emailObservable, passwordObservable) -> {
boolean emailCheck = emailObservable.text().length() >= 3;
boolean passwordCheck = passwordObservable.text().length() >= 3;
return emailCheck && passwordCheck;
}).subscribe(aBoolean -> {
submitButton.setEnabled(aBoolean);
});
A common case that appears in almost every app that requires you to
have an account is the Sign In screen. I wanted to set up form validation
so that only when all fields are valid then a Sign In button would be
enabled.
Repository pattern example
public Observable<List<Repository>> getRepositories() {
Observable<List<Repository>> remote = getRemoteRepositories();
Observable<List<Repository>> local = getLocalRepositories();
return Observable.concat(local, remote);
}
What about Testings?
@Test
public void shouldLoadTwoUsers() throw Exception {
TestSubscriber<User> testSubscriber = new TestSubscriber<>();
databaseHelper.loadUser().subscribe(testSubscriber);
testSubscriber.assertNoErrors();
testSubscriber.assertReceivedOnNext(Arrays.asList(user1, user2))
}
TestSubscriber is a helper class provided by RxJava that we can use for
unit testing, to perform assertions, inspect received events, or wrap a
mocked Subscriber.
@Test
public void testValueCont() {
Observable.just("Hello","RxJava","Testing").subscribe(wordListTestSubscriber);
wordListTestSubscriber.assertValueCount(3);
}