reactive programming with rxjava

Post on 03-Mar-2017

504 Views

Category:

Software

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Reactive Programming with RxJava

Jobaer ChowdhuryTechnical Project Manager, Cefalo

Presented @ JUGBD 6.0 (25/02/2017)

Writing concurrent code in Java is hard.

Writing correct concurrent code is even harder

Unless you’ve read this from cover to cover, twice :-)

What is RxJava?

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.

Brief history or RxReactive 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)

RxJava: Most starred java repository on Github

Many implementationsRx.NET

RxJava

RxJS

RxScala

RxClojure

…. And some others

So let’s get started ...

Observable<T> is the heart of RxJava

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

Let’s compare Observables to Iterables

We all know Iterables, right?Iterable<T> { Iterator<T> iterator();}

Iterator<T> {Boolean hasNext();T next();

}

Observable duality with Iterable Iterable<T>, Iterator<T>

● Get an iterator● hasNext(), next()● Pull based● Sync

Observable<T>, Observer<T>

● Subscribe an observer● onNext(), onError(), onCompleted()● Push based ● Async

Observable<T> examples● Observable<Tweet> ● Observable<Temperature> ● Observable<Person>● Observable<HttpResponse>● Observable<MouseEvent>

….. and so on

Hello Observable!Let’s create an Observable<String> that will emit “Hello, world!”

Observable<String> hello = Observable.create(obs -> {obs.onNext(“Hello, world!”);

});

And we can subscribe to it ...Observable<String> hello = Observable.create(obs -> {

obs.onNext(“Hello, world!”);});

hello.subscribe(s -> {System.out.println(s);

});

We can simplify the creation here Observable<String> hello = Observable.just(“Hello, World!”);

We can also handle errors and completed eventObservable<String> hello = Observable.just(“Hello, World!”);hello.subscribe( val -> {System.out.println(val);}, error -> {System.out.println(“Error occurred”);}, () -> { System.out.println(“Completed”)};})

We’ve seen how we can create Observables, and how to consume them.

Let’s see how we can modify/operate on Observables

Observable.filter exampleObservable<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

Observable.map example// We have a function that gives us an Observable of intsObservable<Integer> ids = searchForArticles(“dhaka”);

// another function that takes an int, and returns an articleArticle loadArticle(Integer articleId) {...}

Observable<Article> result = ids.map(id -> loadArticle(id));

Observable.flatMap example// We have a function that gives us an Observable of intsObservable<Integer> ids = searchForArticles(“dhaka”);

// and a function returns an article wrapped inside ObservableObservable<Article> loadArticle(Integer articleId) {...}

Observable<Article> result = ids.flatMap(id -> loadArticle(id));

Map vs. flatMap

Map vs. flatMap exampleObservable<Integer> ids = searchForArticles(“dhaka”);Observable<Article> loadArticle(Integer articleId) {...}

Observable<Observable<Article>> res = ids.map(this::loadArticle);Observable<Article> result = ids.flatMap(this::loadArticle);

There are many more operators ...

Let’s use Observables to solve a real world problem

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

Let’s assume we have the following (sync) APIList<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)

One way of solving itList<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;

Do you see any problem with this code?

It’s sequential, and may take some time to run.

When an article is loading, nothing prevents us from fetching the like count

of that article, or loading another article, right?

A concurrent solution may save us running time. And we can use

Observables to do so.

Let’s try to use Observables now

Let’s assume now we have an async APIObservable<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});

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

First step: do the searchObservable<Integer> ids = searchForArticles(String query);

….

For each id we need to load the article and fetch the like count.

How do we get the ids out from the Observable?

We don’t, instead we apply some operator to transform the source

Observable to a different one

Let’s go back to few slides ago, and see how we solved the problem sequentially

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;

But we don’t do it like this in rx

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.

In case of Observable.filterObservable<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.

So, let’s get back to the original questionObservable<Integer> ids = searchForArticles(String query);

….

For each id we need to load the article and fetch the like count.

Now we know we need to apply some operator, which one?

We have to apply flatMapObservable<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.

We have to apply flatMapObservable<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?});

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

});})

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()?});

});})

We need to wrap the result inside ObservableObservable<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));

}); });

})

We can remove some nesting here ...

Alternate version using zipObservable<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);});

});})

Using the full power of Java 8 … searchForArticles(query).flatMap(id -> {

return zip(loadArticle(id), fetchLikeCount(id), Article::new); });});

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

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

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

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.

When you have multiple async or concurrent things, depending on each

other, RxJava may be a good candidate.

I only covered the basics, there are way more to learn …

Advanced topics (maybe in some future sessions) A whole lot more operators

Subjects

Schedulers

Backpressure

Implementing custom operators

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

Thank you!

top related