6. What is RxJava?
- A library for composing asynchronous and event based programs
We can write concurrent code using RxJava library without worrying about low
level threads, locks and synchronization.
7. Brief history or Rx
Reactive Extensions: first introduced by Microsoft
RxJava: the jvm implementation was first developed by Netflix
Current version is 2.x
(I’ll talk about version 1.x here)
13. Observable<T>
… a flowing sequence of values
… a stream of events
An observer can subscribe to the Observable
The Observable may call onNext(), onError() or onCompleted() on
the observer
18. Hello Observable!
Let’s create an Observable<String> that will emit “Hello, world!”
Observable<String> hello = Observable.create(obs -> {
obs.onNext(“Hello, world!”);
});
19. And we can subscribe to it ...
Observable<String> hello = Observable.create(obs -> {
obs.onNext(“Hello, world!”);
});
hello.subscribe(s -> {
System.out.println(s);
});
20. We can simplify the creation here
Observable<String> hello = Observable.just(“Hello, World!”);
21. We can also handle errors and completed event
Observable<String> hello = Observable.just(“Hello, World!”);
hello.subscribe(
val -> {System.out.println(val);},
error -> {System.out.println(“Error occurred”);},
() -> { System.out.println(“Completed”)};
})
22. We’ve seen how we can create
Observables, and how to consume them.
25. Observable.filter example
Observable<Integer> numbers = Observable.just(1, 2, 3, 4, 5, 6);
Observable<Integer> result = numbers.filter(num -> num % 2 == 0);
// the result will contain 2, 4, 6 inside the Observables
26.
27. Observable.map example
// We have a function that gives us an Observable of ints
Observable<Integer> ids = searchForArticles(“dhaka”);
// another function that takes an int, and returns an article
Article loadArticle(Integer articleId) {...}
Observable<Article> result = ids.map(id -> loadArticle(id));
28.
29. Observable.flatMap example
// We have a function that gives us an Observable of ints
Observable<Integer> ids = searchForArticles(“dhaka”);
// and a function returns an article wrapped inside Observable
Observable<Article> loadArticle(Integer articleId) {...}
Observable<Article> result = ids.flatMap(id -> loadArticle(id));
35. A real world example
Let’s assume we are working for a news service. And we have the following
requirements.
● Do a search for a term on the search server. The search server will return
some ids
● For each id load the news article from db.
● For each id find out how many likes the article has on social network
● Merge the result from above two steps and send it to view layer
36. Let’s assume we have the following (sync) API
List<Integer> searchForArticles(String query)
PersistentArticle loadArticle(Integer articleId)
Integer fetchLikeCount(Integer articleId)
…. and we can create article by using the db object and like count
Article result = new Article(PersistentArticle, Integer)
37. One way of solving it
List<Integer> searchResult = searchForArticles(“dhaka”);
List<Article> result = new ArrayList<>();
for(Integer id : searchResult) {
PersistentArticle pa = loadArticle(id)
Integer likes = fetchLikeCount(id)
result.add(new Article(pa, likes));
}
return result;
45. Let’s assume now we have an async API
Observable<Integer> searchForArticles(String query)
Observable<PersistentArticle> loadArticle(Integer articleId)
Observable<Integer> fetchLikeCount(Integer articleId)
And we need to come up with the result as the following type.
Observable<Article> result = … ;
// We can use the result like following
result.subscribe(article -> {//update the view with it});
46. Don’t worry about how the async API
was implemented. Just assume it is
present.
(If we only have a sync api, we can still make it async by wrapping things inside
Observables, using Observable.create, just etc.)
47. First step: do the search
Observable<Integer> ids = searchForArticles(String query);
….
For each id we need to load the article and fetch the like count.
48. How do we get the ids out from the
Observable?
49. We don’t, instead we apply some
operator to transform the source
Observable to a different one
50. Let’s go back to few slides ago, and see
how we solved the problem sequentially
51. The traditional solution ...
List<Integer> searchResult = searchForArticles(“dhaka”);
List<Article> result = new ArrayList<>();
for(Integer id : searchResult) {
PersistentArticle pa = loadArticle(id)
Integer likes = fetchLikeCount(id)
result.add(new Article(pa, likes));
}
return result;
55. Many Observable operators take lambda
as parameter.
And we can get the item from inside the
Observable box as a parameter of our
supplied lambda.
56. In case of Observable.filter
Observable<Integer> numbers = Observable.just(1, 2, 3, 4, 5, 6);
numbers.filter(num -> num % 2 == 0);
// The Observable.filter operator takes a lambda as parameter
// We pass it a lambda
// Our supplied lambda will be called for each of the item
// So here the parameter “num” will represent an item inside the box
// This is how we get things out from Observable box.
57. So, let’s get back to the original question
Observable<Integer> ids = searchForArticles(String query);
….
For each id we need to load the article and fetch the like count.
58. Now we know we need to apply some
operator, which one?
59. We have to apply flatMap
Observable<Integer> ids = searchForArticles(query);
ids.flatMap(id -> {
// do something with the id
...
});
So, by applying flatMap, we somehow get the item from inside the Observable
box, as the parameter of our lambda.
60. We have to apply flatMap
Observable<Integer> ids = searchForArticles(query);
ids.flatMap(id -> {
Observable<PersistentArticle> arts = loadArticle(id);
Observable<Integer> likes = fetchLikeCount(id);
//how do we get the article out from inside Observable?
});
61. We need to apply flatMap again ...
Observable<Integer> ids = searchForArticles(query);
ids.flatMap(id -> {
Observable<PersistentArticle> arts = loadArticle(id);
Observable<Integer> likes = fetchLikeCount(id);
return arts.flatMap(art -> {
// and now we need to get the likes out
});
})
62. And flatMap again ...
Observable<Integer> ids = searchForArticles(query);
ids.flatMap(id -> {
Observable<PersistentArticle> arts = loadArticle(id);
Observable<Integer> likes = fetchLikeCount(id);
return arts.flatMap(art -> {
return likes.flatMap(like -> {
// now we have everything to make an Article object
// so what do we return here? A new Article()?
});
});
})
63. We need to wrap the result inside Observable
Observable<Integer> ids = searchForArticles(query);
ids.flatMap(id -> {
Observable<PersistentArticle> arts = loadArticle(id);
Observable<Integer> likes = fetchLikeCount(id);
return arts.flatMap(art -> {
return likes.flatMap(like -> {
return Observable.just(new Article(art, like));
});
});
})
66. Alternate version using zip
Observable<Integer> ids = searchForArticles(query);
ids.flatMap(id -> {
Observable<PersistentArticle> arts = loadArticle(id);
Observable<Integer> likes = fetchLikeCount(id);
return Observable.zip(arts, likes, (art, lc) -> {
return new Article(art, lc);
});
});
})
67. Using the full power of Java 8 …
searchForArticles(query).flatMap(id -> {
return zip(loadArticle(id),
fetchLikeCount(id),
Article::new);
});
});
68. Using the full power of Java 8 …
searchForArticles(query).flatMap(id -> {
return zip(loadArticle(id),
fetchLikeCount(id),
Article::new);
});
});
This is the solution we desire. I find this code beautiful. And it’s (mostly)
concurrent (depending on the implementation of the search, load, etc.).
69. Keep these in mind while using RxJava
● Observables can emit zero, one or more items
● Observables can be of infinite stream of values/events
● Observables can complete without returning anything, Observable.empty().
● Observables can emit some items and then call onError() to terminate
abnormally
● If onError() is called, then onCompleted will not be called
70. Keep these in mind while using RxJava
● Observables are by default lazy. If no subscriber is subscribed, then nothing
will be executed.
● By default they run in the same thread from which the subscriber is called
● You can change the subscriber thread by calling subscribeOn()
● You can change the observer thread by calling observeOn()
● There are some built in Schedulers (similar to thread pool). For example
Schedulers.io(), Schedulers.computation().
● A single Observable issues notifications to observers serially (not in parallel).
71. A better version of the previous code
searchForArticles(query).flatMap(id -> {
return Observable.just(id)
.subscribeOn(Schedulers.io())
.flatMap(i ->{
return zip(loadArticle(i),
fetchLikeCount(i),
Article::new);
});
});
});
In fact this is the concurrent version, although it lost it’s beauty a bit :-)
Hoping to cover this in a future session.
72. When you have multiple async or
concurrent things, depending on each
other, RxJava may be a good candidate.
73. I only covered the basics, there are way
more to learn …
74. Advanced topics (maybe in some future sessions)
A whole lot more operators
Subjects
Schedulers
Backpressure
Implementing custom operators
75. Have a look into
● CompletableFuture (introduced in java 8)
● Reactive streams specification (which RxJava 2.x implemented)
● java.util.concurrent.Flow api (coming in java 9)
76. Reference
● Collection of all tutorials, http://reactivex.io/tutorials.html
● Reactive programming with RxJava book,
http://shop.oreilly.com/product/0636920042228.do
● The RxJava contract, http://reactivex.io/documentation/contract.html