SlideShare a Scribd company logo
RxJava Applied:
Concise Examples where It Shines
Igor Lozynskyi
JavaDay Kyiv - Oct 14-15, 2016
Software Engineer at GlobalLogic
Presentation’s home
https://github.com/aigor/rx-presentation
Outline
● RxJava vs Java 8 Streams
● RxJava usage example
Pre Java 8 data processing
interface Iterator<T> {
T next();
boolean hasNext();
void remove();
}
Pre Java 8 data processing
List<Employee> employees = service.getEmployees();
Map<Integer, List<Employee>> ageDistribution = new HashMap<>();
for (Employee employee : employees) {
if (employee.getAge() > 25){
List<Employee> thisAge = ageDistribution.get(employee.getAge());
if (thisAge != null){
thisAge.add(employee);
} else{
List<Employee> createThisAge = new ArrayList<>();
createThisAge.add(employee);
ageDistribution.put(employee.getAge(), createThisAge);
}
}
}
System.out.println(ageDistribution);
Java 8 Stream ...
● Connects data source and client
● Do not hold any data
● Implements map / filter / reduce pattern
● Enforces functional style of data processing
Stream collectors
List<Employee> employees = service.getEmployees();
Map<Integer, List<Employee>> ageDistribution = new HashMap<>();
for (Employee employee : employees) {
if (employee.getAge() > 25) {
List<Employee> thisAge = ageDistribution.get(employee.getAge());
if (thisAge != null){
thisAge.add(employee);
} else {
List<Employee> createThisAge = new ArrayList<>();
createThisAge.add(employee);
ageDistribution.put(employee.getAge(), createThisAge);
}
}
}
System.out.println(ageDistribution);
Stream collectors
List<Employee> employees = service.getEmployees();
Map<Integer, List<Employee>> ageDistribution =
employees.stream()
.filter(e -> e.getAge() > 25)
.collect(Collectors.groupingBy(Employee::getAge));
System.out.println(ageDistribution);
Stream collectors
List<Employee> employees = service.getEmployees();
Map<Integer, Long> ageDistribution =
employees.stream()
.filter(e -> e.getAge() > 25)
.collect(Collectors.groupingBy(
Employee::getAge,
Collectors.counting()
));
Stream sources
Stream<String> stream1 = Arrays.asList("A", "B").stream();
Stream<String> stream2 = Stream.of("Q", "P", "R");
IntStream chars = "some text".chars();
Stream<String> words
= Pattern.compile(" ").splitAsStream("some other text");
Non-standard stream sources
Can we use non-standard stream sources?
?
Stream generator
Stream
.generate(() -> UUID.randomUUID().toString())
.limit(4)
.forEach(System.out::println);
Spliterator interface
public interface Spliterator<T> {
boolean tryAdvance(Consumer<? super T> action);
Spliterator<T> trySplit();
long estimateSize();
int characteristics();
}
class RandomSpliterator implements Spliterator<String> {
public boolean tryAdvance(Consumer<? super String> action) {
action.accept(UUID.randomUUID().toString());
return true;
}
// for parallel streams
public Spliterator<String> trySplit() {
return null;
}
public long estimateSize() { return Long.MAX_VALUE; };
public int characteristics() { return NONNULL | DISTINCT; }
}
Generate sequence: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, ...
IntStream sequence =
IntStream.rangeClosed(1, 50)
.flatMap(i ->
IntStream.iterate(i, identity()).limit(i)
);
sequence.forEach(System.out::println);
Stream API is powerful!
Stream API - async processing
getEmployeeIds().stream()
.map(this::doHttpRequest)
.collect(Collectors.toList())
Output:
[main] processing request: c677c497
[main] processing request: 3b5320a9
[main] processing request: 9248b92e
[main] processing request: 97a68a53
Stream API - async processing
getEmployeeIds().stream()
.parallel()
.map(this::doHttpRequest)
.collect(Collectors.toList())
Output:
[main] processing request: 7674da72
[ForkJoinPool.commonPool-worker-2] processing request: 747ae948
[ForkJoinPool.commonPool-worker-1] processing request: 33fe0bac
[ForkJoinPool.commonPool-worker-3] processing request: 812f69f3
[main] processing request: 11dda466
[ForkJoinPool.commonPool-worker-2] processing request: 12e22a10
[ForkJoinPool.commonPool-worker-1] processing request: e2b324f9
[ForkJoinPool.commonPool-worker-3] processing request: 8f9f8a97
Stream API - async processing
ForkJoinPool forkJoinPool = new ForkJoinPool(80);
forkJoinPool.submit(() ->
getEmployeeIds().stream()
.parallel()
.map(this::doHttpRequest)
.collect(Collectors.toList())
).get();
Stream API has some limitations
Reactive Streams: what the difference
RxJava
http://reactivex.io
https://github.com/ReactiveX/RxJava
17,700 stars on GitHub
Short history
● From 2009 for .NET
● From 2013 for JVM (latest: 1.2.1, Oct 5, 2016)
● Now a lot of other languages
RxJava Observer
interface Observer<T> {
void onNext(T t);
void onCompleted();
void onError(Throwable e);
}
RxJava Subscription
interface Subscription {
void unsubscribe();
boolean isUnsubscribed();
}
Observable
● Central class in RxJava library
● It’s big: ~ 10k lines of code (with comments)
● It’s complex: ~ 100 static methods, ~ 150 non-static
Subscription sub =
Observable
.create(s -> {
s.onNext("A");
s.onNext("B");
s.onCompleted();
})
.subscribe(m -> log.info("Message received: " + m),
e -> log.warning("Error: " + e.getMessage()),
() -> log.info("Done!"));
Output:
Message received: A
Message received: B
Done!
Observable<Integer> empty = Observable.empty();
Observable<Integer> never = Observable.never();
Observable<Integer> error = Observable.error(exception);
Observable useful for tests
never() - never emit anything (no data, no errors)
public static <T, Resource> Observable<T> using(
final Func0<Resource> resourceFactory,
final Func1<Resource, Observable<T>> observableFactory,
final Action1<Resource> disposeAction
){ }
Observable from resource
Observable
.from(Arrays.asList(1, 2, 5, 7, 8, 12, 3, 6, 7, 8))
.filter(i -> (i > 3 && i < 8))
.forEach(System.out::println);
Output:
5
7
6
7
Marble diagram: filter
Marble diagram: last
Marble diagram: reduce
Marble diagram: buffer
Marble diagram: timer
Marble diagram: interval
Time series
Observable<Long> timer = Observable.timer(2, TimeUnit.SECONDS);
Observable<Long> interval = Observable.interval(1, TimeUnit.SECONDS);
timer.forEach(System.out::println);
interval.forEach(System.out::println);
Output:
Process finished with exit code 0
Time series
Observable<Long> timer = Observable.timer(2, TimeUnit.SECONDS);
Observable<Long> interval = Observable.interval(1, TimeUnit.SECONDS);
timer.forEach(i -> System.out.println(currentThread().getName() + " - " + i));
interval.forEach(i -> System.out.println(currentThread().getName() + " - " + i));
Output:
RxComputationThreadPool-2 - 0
RxComputationThreadPool-1 - 0
RxComputationThreadPool-2 - 1
Process finished with exit code 0
Thread.sleep(2000);
Schedulers
public final class Schedulers {
public static Scheduler immediate() {...}
public static Scheduler trampoline() {...}
public static Scheduler newThread() {...}
public static Scheduler computation() {...}
public static Scheduler io() {...}
public static TestScheduler test() {...}
public static Scheduler from(Executor executor) {...}
}
Schedulers
Observable.from("one", "two", "three", "four", "five")
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(/* an Observer */);
Marble diagram: merge
Merge
Observable<Integer> odds = Observable
.just(1, 3, 5).subscribeOn(Schedulers.io());
Observable<Integer> evens = Observable.just(2, 4, 6);
Observable.merge(odds, evens)
.subscribe(
System.out::println,
System.err::println,
() -> System.out.println("Finished"));
Marble diagram: zip
Zip
Observable<String> odds = Observable.just("A", "B", "C", "D");
Observable<Integer> evens = Observable.just(2, 4, 6);
Observable.zip(odds, evens, (a, b) -> a + "-" + b)
.subscribe(
System.out::println,
System.err::println,
() -> System.out.println("Finished"));
Output:
A-2
B-4
C-6
Finished
Cold & Hot Observables
● Cold Observable emits events only having subscriber
● Hot Observable emits events even without subscribers
Backpressure
Backpressure
Backpreasure is a way to slow down emission of elements
It can act on observing side
Strategies:
● Buffering items (buffer, window)
● Skipping items (sampling, throttling, etc)
● Request for specific number of elements
Backpressure: request for elements
public interface Producer {
void request(long n);
}
Stream API vs RxJava
RxJava:
● allows to process data in chosen thread, this is useful for IO,
computations, specialized threads (GUI threads),
● allows synchronization on clocks and application events,
● works in push mode, producer initiates data transfer, but consumer may
control data flow via backpressure.
Stream API:
● tuned for hot data processing,
● good for parallel computation,
● has rich set of collectors for data.
Shakespeare Plays Scrabble Benchmark (throughput)
Non-Parallel Streams 44.995 ± 1.718 ms/op
Parallel Streams 14.095 ± 0.616 ms/op
RxJava 310.378 ± 9.688 ms/op
RxJava (2.0.0-RC4) 156.334 ± 8.423 ms/op
Performance
Based on JMH benchmark by Jose Paumard
Scenarios where RxJava shines
● Observables are better callbacks (easily wrap callbacks)
● Observables are highly composable (on contrast with callbacks)
● Provide async stream of data (on contrast with CompletableFuture)
● Observables can handle errors (have retry / backup strategies)
● Give complete control over running threads
● Good for managing IO rich application workflows
● Perfect for Android development (no Java 8 required, retrolambda compatible)
● Netflix uses RxJava for most their internal APIs
Request flow
Created with draw.io
Stream API and RxJava are friends!
You can easily build Observable from Stream
Iterator<T> iterator = ...;
Observable<T> observable = Observable.from(() -> iterator);
You can map Observable to Stream by implementing adapter
public static<T> Iterator<T> of(Observable<? extends T> ob){
class Adapter implements Iterator<T>, Consumer<T> {
...
}
return new Adapter();
}
External libraries that work with RxJava
● hystrix - latency and fault tolerance bulkheading library
● camel RX - to reuse Apache Camel components
● rxjava-http-tail - allows you to follow logs over HTTP
● mod-rxvertx - extension for VertX that provides support Rx
● rxjava-jdbc - use RxJava with JDBC to stream ResultSets
● rtree - immutable in-memory R-tree and R*-tree with RxJava
● and many more ...
RxJava is not new
JDeferred
CompletableFuture<T>
Scala.Rx
Scala Actors
Spring Integration
What’s around?
● Google Agera - reactive streams for Android by Google
● Project Reactor - flow API implementation by Pivotal & Spring
● Akka-Streams - Akka based streams
● Monix - Scala based implementation of Reactive Streams
● Vert.x, Lagom - if you want more than streams
Let’s build something with RxJava
Use case: Stream of tweets
Created with Balsamiq Mockups
Twitter API
Twitter Stream API (WebSocket alike):
● Doc: https://dev.twitter.com/streaming/overview
● Library: com.twitter:hbc-core:2.2.0
Twitter REST API:
● GET https://api.twitter.com/1.1/users/show.json?screen_name=jeeconf
● GET https://api.twitter.com/1.1/search/tweets.json?q=from:jeeconf
Let’s look at entities
class Tweet {
String text;
int favorite_count;
String author;
int author_followers;
}
class Profile {
String screen_name;
String name;
String location;
int statuses_count;
int followers_count;
}
class UserWithTweet {
Profile profile;
Tweet tweet;
}
Marble diagram
Profile getUserProfile(String screenName) {
ObjectMapper om = new ObjectMapper();
return (Profile) om.readValue(om.readTree(
Unirest.get(API_BASE_URL + "users/show.json")
.queryString("screen_name", screenName)
.header("Authorization", bearerAuth(authToken.get()))
.asString()
.getBody()),
Profile.class);
}
Get user profile synchronously
Get user profile asynchronously
Observable<Profile> getUserProfile(String screenName) {
return Observable.fromCallable(() -> {
ObjectMapper om = new ObjectMapper();
return (Profile) om.readValue(om.readTree(
Unirest.get(API_BASE_URL + "users/show.json")
.queryString("screen_name", screenName)
.header("Authorization", bearerAuth(authToken.get()))
.asString()
.getBody()),
Profile.class);
});
}
Add some errors handling
Observable<Profile> getUserProfile(String screenName) {
if (authToken.isPresent()) {
return Observable.fromCallable(() -> {
ObjectMapper om = new ObjectMapper();
return (Profile) om.readValue(om.readTree(
Unirest.get(API_BASE_URL + "users/show.json")
.queryString("screen_name", screenName)
.header("Authorization", bearerAuth(authToken.get()))
.asString()
.getBody()),
Profile.class);
}).doOnCompleted(() -> log("getUserProfile completed for: " + screenName));
} else {
return Observable.error(new RuntimeException("Can not connect to twitter"));
}
}
Let’s do something concurrently
Illustration: Arsenal Firearms S.r.l.
RxJava applied [JavaDay Kyiv 2016]
Get data concurrently
Observable<UserWithTweet> getUserAndPopularTweet(String author){
return Observable.just(author)
.flatMap(u -> {
Observable<Profile> profile = client.getUserProfile(u)
.subscribeOn(Schedulers.io());
Observable<Tweet> tweet = client.getUserRecentTweets(u)
.defaultIfEmpty(null)
.reduce((t1, t2) ->
t1.retweet_count > t2.retweet_count ? t1 : t2)
.subscribeOn(Schedulers.io());
return Observable.zip(profile, tweet, UserWithTweet::new);
});
}
RxJava applied [JavaDay Kyiv 2016]
Let’s subscribe on stream of tweets!
streamClient.getStream("RxJava", "JavaDay", "Java")
streamClient.getStream("RxJava", "JavaDay", "Java", "Trump", "Hillary")
streamClient.getStream("RxJava", "JavaDay", "Java", "Trump", "Hillary")
.distinctUntilChanged()
.map(p -> p.author)
.flatMap(name -> getUserAndPopularTweet(name))
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.immediate())
.subscribe(p -> log.info("The most popular tweet of user "
+ p.profile.name + ": " + p.tweet));
.scan((u1, u2) -> u1.author_followers > u2.author_followers ? u1 : u2)
streamClient.getStream("RxJava", "JavaDay", "Java", "Trump")
.scan((u1, u2) -> u1.author_followers > u2.author_followers ? u1 : u2)
.distinctUntilChanged()
.map(p -> p.author)
.flatMap(name -> {
Observable<Profile> profile = client.getUserProfile(name)
.subscribeOn(Schedulers.io());
Observable<Tweet> tweet = client.getUserRecentTweets(name)
.defaultIfEmpty(null)
.reduce((t1, t2) ->
t1.retweet_count > t2.retweet_count ? t1 : t2)
.subscribeOn(Schedulers.io());
return Observable.zip(profile, tweet, UserWithTweet::new);
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.immediate())
.subscribe(p -> log.info("The most popular tweet of user "
+ p.profile.name + ": " + p.tweet));
Marble diagram
@Test public void correctlyJoinsHttpResults() throws Exception {
String testUser = "testUser";
Profile profile = new Profile("u1", "Name", "USA", 10, 20, 30);
Tweet tweet1 = new Tweet("text-1", 10, 20, testUser, 30);
Tweet tweet2 = new Tweet("text-2", 40, 50, testUser, 30);
TwitterClient client = mock(TwitterClient.class);
when(client.getUserProfile(testUser)).thenReturn(Observable.just(profile));
when(client.getUserRecentTweets(testUser)).thenReturn(Observable.just(tweet1, tweet2));
TestSubscriber<UserWithTweet> testSubscriber = new TestSubscriber<>();
new Solutions().getUserAndPopularTweet(client, testUser).subscribe(testSubscriber);
testSubscriber.awaitTerminalEvent();
assertEquals(singletonList(new UserWithTweet(profile, tweet2)),
testSubscriber.getOnNextEvents());
}
Don’t believe it works?
● API is big (150+ methods to remember)
● Requires to understand underlying magic
● Hard to debug
● Don’t forget about back pressure
Conclusions: pitfalls
● It is functional, it is reactive*
● Good for integration scenarios
● Allows to control execution threads
● Easy to compose workflows
● Easy to integrate into existing solutions
● Easy Ok to test
* RxJava is inspired by FRP (Functional Reactive Programming), but doesn’t implement it
Conclusions: strength
● RxJava 2.0 in few weeks!
● RxJava 2.0 is better and faster
● RxJava 2.0 is Java 9 Flow API compatible
● We will see more and more reactive streams
Conclusions: future
Q&A
https://github.com/aigor/rx-presentation
Thanks
Presentation template by SlidesCarnival
@siromaha
aigooor@gmail.com

More Related Content

What's hot (20)

PPTX
Introduction to Reactive Java
Tomasz Kowalczewski
 
PDF
Reactive programming with RxJava
Jobaer Chowdhury
 
PDF
Practical RxJava for Android
Tomáš Kypta
 
PPTX
Reactive Java (GeeCON 2014)
Tomasz Kowalczewski
 
PDF
Reactive programming on Android
Tomáš Kypta
 
PDF
Non Blocking I/O for Everyone with RxJava
Frank Lyaruu
 
PDF
rx-java-presentation
Mateusz Bukowicz
 
PPTX
The Road To Reactive with RxJava JEEConf 2016
Frank Lyaruu
 
PDF
Functional Reactive Programming in Clojurescript
Leonardo Borges
 
PPTX
Reactive Programming on Android - RxAndroid - RxJava
Ali Muzaffar
 
PDF
Streams, Streams Everywhere! An Introduction to Rx
Andrzej Sitek
 
PDF
RxJava in practice
Javier Gamarra
 
PDF
RxJava 2.0 介紹
Kros Huang
 
PPTX
Rx java in action
Pratama Nur Wijaya
 
PDF
Rxjava 介紹與 Android 中的 RxJava
Kros Huang
 
PPTX
Reactive programming with RxAndroid
Savvycom Savvycom
 
PPTX
Reactive Programming in Java 8 with Rx-Java
Kasun Indrasiri
 
PPTX
Real world functional reactive programming
Eric Polerecky
 
PDF
JavaOne 2013: Java 8 - The Good Parts
Konrad Malawski
 
PDF
RxJava for Android - GDG DevFest Ukraine 2015
Constantine Mars
 
Introduction to Reactive Java
Tomasz Kowalczewski
 
Reactive programming with RxJava
Jobaer Chowdhury
 
Practical RxJava for Android
Tomáš Kypta
 
Reactive Java (GeeCON 2014)
Tomasz Kowalczewski
 
Reactive programming on Android
Tomáš Kypta
 
Non Blocking I/O for Everyone with RxJava
Frank Lyaruu
 
rx-java-presentation
Mateusz Bukowicz
 
The Road To Reactive with RxJava JEEConf 2016
Frank Lyaruu
 
Functional Reactive Programming in Clojurescript
Leonardo Borges
 
Reactive Programming on Android - RxAndroid - RxJava
Ali Muzaffar
 
Streams, Streams Everywhere! An Introduction to Rx
Andrzej Sitek
 
RxJava in practice
Javier Gamarra
 
RxJava 2.0 介紹
Kros Huang
 
Rx java in action
Pratama Nur Wijaya
 
Rxjava 介紹與 Android 中的 RxJava
Kros Huang
 
Reactive programming with RxAndroid
Savvycom Savvycom
 
Reactive Programming in Java 8 with Rx-Java
Kasun Indrasiri
 
Real world functional reactive programming
Eric Polerecky
 
JavaOne 2013: Java 8 - The Good Parts
Konrad Malawski
 
RxJava for Android - GDG DevFest Ukraine 2015
Constantine Mars
 

Viewers also liked (7)

PDF
Reactive Thinking in Java
Yakov Fain
 
PPTX
04 pig data operations
Subhas Kumar Ghosh
 
PPTX
Issues in knowledge representation
Sravanthi Emani
 
PDF
Intro to Graph Databases Using Tinkerpop, TitanDB, and Gremlin
Caleb Jones
 
PPTX
Knowledge representation in AI
Vishal Singh
 
PPTX
Knowledge representation and Predicate logic
Amey Kerkar
 
PPTX
Introduction to Machine Learning
Rahul Jain
 
Reactive Thinking in Java
Yakov Fain
 
04 pig data operations
Subhas Kumar Ghosh
 
Issues in knowledge representation
Sravanthi Emani
 
Intro to Graph Databases Using Tinkerpop, TitanDB, and Gremlin
Caleb Jones
 
Knowledge representation in AI
Vishal Singh
 
Knowledge representation and Predicate logic
Amey Kerkar
 
Introduction to Machine Learning
Rahul Jain
 
Ad

Similar to RxJava applied [JavaDay Kyiv 2016] (20)

PDF
Developing streaming applications with apache apex (strata + hadoop world)
Apache Apex
 
PDF
[245] presto 내부구조 파헤치기
NAVER D2
 
PDF
Presto anatomy
Dongmin Yu
 
PPTX
Javaone 2016 : Supercharge your (reactive) Streams
John McClean
 
PDF
A Deep Dive into Query Execution Engine of Spark SQL
Databricks
 
PPTX
Flink 0.10 @ Bay Area Meetup (October 2015)
Stephan Ewen
 
ODP
Finagle and Java Service Framework at Pinterest
Pavan Chitumalla
 
PDF
Functional UIs with Java 8 and Vaadin JavaOne2014
hezamu
 
PPTX
Where the wild things are - Benchmarking and Micro-Optimisations
Matt Warren
 
PDF
PgQ Generic high-performance queue for PostgreSQL
elliando dias
 
PDF
JS Fest 2019. Anjana Vakil. Serverless Bebop
JSFestUA
 
PDF
GDG DevFest 2015 - Reactive approach for slowpokes
Sergey Tarasevich
 
PDF
What is new in java 8 concurrency
kshanth2101
 
PDF
Intro to Retrofit 2 and RxJava2
Fabio Collini
 
PDF
How to Think in RxJava Before Reacting
IndicThreads
 
PDF
Continuous Application with Structured Streaming 2.0
Anyscale
 
PDF
[JEEConf-2017] RxJava as a key component in mature Big Data product
Igor Lozynskyi
 
PPTX
Lambdas puzzler - Peter Lawrey
JAXLondon_Conference
 
PPTX
Realtime Statistics based on Apache Storm and RocketMQ
Xin Wang
 
PDF
Nodejs性能分析优化和分布式设计探讨
flyinweb
 
Developing streaming applications with apache apex (strata + hadoop world)
Apache Apex
 
[245] presto 내부구조 파헤치기
NAVER D2
 
Presto anatomy
Dongmin Yu
 
Javaone 2016 : Supercharge your (reactive) Streams
John McClean
 
A Deep Dive into Query Execution Engine of Spark SQL
Databricks
 
Flink 0.10 @ Bay Area Meetup (October 2015)
Stephan Ewen
 
Finagle and Java Service Framework at Pinterest
Pavan Chitumalla
 
Functional UIs with Java 8 and Vaadin JavaOne2014
hezamu
 
Where the wild things are - Benchmarking and Micro-Optimisations
Matt Warren
 
PgQ Generic high-performance queue for PostgreSQL
elliando dias
 
JS Fest 2019. Anjana Vakil. Serverless Bebop
JSFestUA
 
GDG DevFest 2015 - Reactive approach for slowpokes
Sergey Tarasevich
 
What is new in java 8 concurrency
kshanth2101
 
Intro to Retrofit 2 and RxJava2
Fabio Collini
 
How to Think in RxJava Before Reacting
IndicThreads
 
Continuous Application with Structured Streaming 2.0
Anyscale
 
[JEEConf-2017] RxJava as a key component in mature Big Data product
Igor Lozynskyi
 
Lambdas puzzler - Peter Lawrey
JAXLondon_Conference
 
Realtime Statistics based on Apache Storm and RocketMQ
Xin Wang
 
Nodejs性能分析优化和分布式设计探讨
flyinweb
 
Ad

Recently uploaded (20)

PPTX
Gall bladder, Small intestine and Large intestine.pptx
rekhapositivity
 
PDF
IMP NAAC-Reforms-Stakeholder-Consultation-Presentation-on-Draft-Metrics-Unive...
BHARTIWADEKAR
 
PDF
1, 2, 3… E MAIS UM CICLO CHEGA AO FIM!.pdf
Colégio Santa Teresinha
 
PPTX
How to Configure Prepayments in Odoo 18 Sales
Celine George
 
PDF
Generative AI: it's STILL not a robot (CIJ Summer 2025)
Paul Bradshaw
 
PPT
Talk on Critical Theory, Part II, Philosophy of Social Sciences
Soraj Hongladarom
 
PDF
ARAL-Orientation_Morning-Session_Day-11.pdf
JoelVilloso1
 
PPTX
2025 Winter SWAYAM NPTEL & A Student.pptx
Utsav Yagnik
 
PDF
community health nursing question paper 2.pdf
Prince kumar
 
PPTX
Pyhton with Mysql to perform CRUD operations.pptx
Ramakrishna Reddy Bijjam
 
PDF
Federal dollars withheld by district, charter, grant recipient
Mebane Rash
 
PPTX
PPT on the Development of Education in the Victorian England
Beena E S
 
PPTX
How to Manage Promotions in Odoo 18 Sales
Celine George
 
PPTX
How to Configure Access Rights of Manufacturing Orders in Odoo 18 Manufacturing
Celine George
 
PPTX
How to Manage Large Scrollbar in Odoo 18 POS
Celine George
 
PPTX
STAFF DEVELOPMENT AND WELFARE: MANAGEMENT
PRADEEP ABOTHU
 
PPTX
How to Manage Access Rights & User Types in Odoo 18
Celine George
 
PPTX
A PPT on Alfred Lord Tennyson's Ulysses.
Beena E S
 
PPTX
Unit 2 COMMERCIAL BANKING, Corporate banking.pptx
AnubalaSuresh1
 
PDF
BÀI TẬP BỔ TRỢ TIẾNG ANH 8 - GLOBAL SUCCESS - CẢ NĂM - NĂM 2024 (VOCABULARY, ...
Nguyen Thanh Tu Collection
 
Gall bladder, Small intestine and Large intestine.pptx
rekhapositivity
 
IMP NAAC-Reforms-Stakeholder-Consultation-Presentation-on-Draft-Metrics-Unive...
BHARTIWADEKAR
 
1, 2, 3… E MAIS UM CICLO CHEGA AO FIM!.pdf
Colégio Santa Teresinha
 
How to Configure Prepayments in Odoo 18 Sales
Celine George
 
Generative AI: it's STILL not a robot (CIJ Summer 2025)
Paul Bradshaw
 
Talk on Critical Theory, Part II, Philosophy of Social Sciences
Soraj Hongladarom
 
ARAL-Orientation_Morning-Session_Day-11.pdf
JoelVilloso1
 
2025 Winter SWAYAM NPTEL & A Student.pptx
Utsav Yagnik
 
community health nursing question paper 2.pdf
Prince kumar
 
Pyhton with Mysql to perform CRUD operations.pptx
Ramakrishna Reddy Bijjam
 
Federal dollars withheld by district, charter, grant recipient
Mebane Rash
 
PPT on the Development of Education in the Victorian England
Beena E S
 
How to Manage Promotions in Odoo 18 Sales
Celine George
 
How to Configure Access Rights of Manufacturing Orders in Odoo 18 Manufacturing
Celine George
 
How to Manage Large Scrollbar in Odoo 18 POS
Celine George
 
STAFF DEVELOPMENT AND WELFARE: MANAGEMENT
PRADEEP ABOTHU
 
How to Manage Access Rights & User Types in Odoo 18
Celine George
 
A PPT on Alfred Lord Tennyson's Ulysses.
Beena E S
 
Unit 2 COMMERCIAL BANKING, Corporate banking.pptx
AnubalaSuresh1
 
BÀI TẬP BỔ TRỢ TIẾNG ANH 8 - GLOBAL SUCCESS - CẢ NĂM - NĂM 2024 (VOCABULARY, ...
Nguyen Thanh Tu Collection
 

RxJava applied [JavaDay Kyiv 2016]

  • 1. RxJava Applied: Concise Examples where It Shines Igor Lozynskyi JavaDay Kyiv - Oct 14-15, 2016 Software Engineer at GlobalLogic
  • 3. Outline ● RxJava vs Java 8 Streams ● RxJava usage example
  • 4. Pre Java 8 data processing interface Iterator<T> { T next(); boolean hasNext(); void remove(); }
  • 5. Pre Java 8 data processing List<Employee> employees = service.getEmployees(); Map<Integer, List<Employee>> ageDistribution = new HashMap<>(); for (Employee employee : employees) { if (employee.getAge() > 25){ List<Employee> thisAge = ageDistribution.get(employee.getAge()); if (thisAge != null){ thisAge.add(employee); } else{ List<Employee> createThisAge = new ArrayList<>(); createThisAge.add(employee); ageDistribution.put(employee.getAge(), createThisAge); } } } System.out.println(ageDistribution);
  • 6. Java 8 Stream ... ● Connects data source and client ● Do not hold any data ● Implements map / filter / reduce pattern ● Enforces functional style of data processing
  • 7. Stream collectors List<Employee> employees = service.getEmployees(); Map<Integer, List<Employee>> ageDistribution = new HashMap<>(); for (Employee employee : employees) { if (employee.getAge() > 25) { List<Employee> thisAge = ageDistribution.get(employee.getAge()); if (thisAge != null){ thisAge.add(employee); } else { List<Employee> createThisAge = new ArrayList<>(); createThisAge.add(employee); ageDistribution.put(employee.getAge(), createThisAge); } } } System.out.println(ageDistribution);
  • 8. Stream collectors List<Employee> employees = service.getEmployees(); Map<Integer, List<Employee>> ageDistribution = employees.stream() .filter(e -> e.getAge() > 25) .collect(Collectors.groupingBy(Employee::getAge)); System.out.println(ageDistribution);
  • 9. Stream collectors List<Employee> employees = service.getEmployees(); Map<Integer, Long> ageDistribution = employees.stream() .filter(e -> e.getAge() > 25) .collect(Collectors.groupingBy( Employee::getAge, Collectors.counting() ));
  • 10. Stream sources Stream<String> stream1 = Arrays.asList("A", "B").stream(); Stream<String> stream2 = Stream.of("Q", "P", "R"); IntStream chars = "some text".chars(); Stream<String> words = Pattern.compile(" ").splitAsStream("some other text");
  • 11. Non-standard stream sources Can we use non-standard stream sources? ?
  • 12. Stream generator Stream .generate(() -> UUID.randomUUID().toString()) .limit(4) .forEach(System.out::println);
  • 13. Spliterator interface public interface Spliterator<T> { boolean tryAdvance(Consumer<? super T> action); Spliterator<T> trySplit(); long estimateSize(); int characteristics(); }
  • 14. class RandomSpliterator implements Spliterator<String> { public boolean tryAdvance(Consumer<? super String> action) { action.accept(UUID.randomUUID().toString()); return true; } // for parallel streams public Spliterator<String> trySplit() { return null; } public long estimateSize() { return Long.MAX_VALUE; }; public int characteristics() { return NONNULL | DISTINCT; } }
  • 15. Generate sequence: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, ... IntStream sequence = IntStream.rangeClosed(1, 50) .flatMap(i -> IntStream.iterate(i, identity()).limit(i) ); sequence.forEach(System.out::println);
  • 16. Stream API is powerful!
  • 17. Stream API - async processing getEmployeeIds().stream() .map(this::doHttpRequest) .collect(Collectors.toList()) Output: [main] processing request: c677c497 [main] processing request: 3b5320a9 [main] processing request: 9248b92e [main] processing request: 97a68a53
  • 18. Stream API - async processing getEmployeeIds().stream() .parallel() .map(this::doHttpRequest) .collect(Collectors.toList()) Output: [main] processing request: 7674da72 [ForkJoinPool.commonPool-worker-2] processing request: 747ae948 [ForkJoinPool.commonPool-worker-1] processing request: 33fe0bac [ForkJoinPool.commonPool-worker-3] processing request: 812f69f3 [main] processing request: 11dda466 [ForkJoinPool.commonPool-worker-2] processing request: 12e22a10 [ForkJoinPool.commonPool-worker-1] processing request: e2b324f9 [ForkJoinPool.commonPool-worker-3] processing request: 8f9f8a97
  • 19. Stream API - async processing ForkJoinPool forkJoinPool = new ForkJoinPool(80); forkJoinPool.submit(() -> getEmployeeIds().stream() .parallel() .map(this::doHttpRequest) .collect(Collectors.toList()) ).get();
  • 20. Stream API has some limitations
  • 21. Reactive Streams: what the difference
  • 23. Short history ● From 2009 for .NET ● From 2013 for JVM (latest: 1.2.1, Oct 5, 2016) ● Now a lot of other languages
  • 24. RxJava Observer interface Observer<T> { void onNext(T t); void onCompleted(); void onError(Throwable e); }
  • 25. RxJava Subscription interface Subscription { void unsubscribe(); boolean isUnsubscribed(); }
  • 26. Observable ● Central class in RxJava library ● It’s big: ~ 10k lines of code (with comments) ● It’s complex: ~ 100 static methods, ~ 150 non-static
  • 27. Subscription sub = Observable .create(s -> { s.onNext("A"); s.onNext("B"); s.onCompleted(); }) .subscribe(m -> log.info("Message received: " + m), e -> log.warning("Error: " + e.getMessage()), () -> log.info("Done!")); Output: Message received: A Message received: B Done!
  • 28. Observable<Integer> empty = Observable.empty(); Observable<Integer> never = Observable.never(); Observable<Integer> error = Observable.error(exception); Observable useful for tests never() - never emit anything (no data, no errors)
  • 29. public static <T, Resource> Observable<T> using( final Func0<Resource> resourceFactory, final Func1<Resource, Observable<T>> observableFactory, final Action1<Resource> disposeAction ){ } Observable from resource
  • 30. Observable .from(Arrays.asList(1, 2, 5, 7, 8, 12, 3, 6, 7, 8)) .filter(i -> (i > 3 && i < 8)) .forEach(System.out::println); Output: 5 7 6 7
  • 37. Time series Observable<Long> timer = Observable.timer(2, TimeUnit.SECONDS); Observable<Long> interval = Observable.interval(1, TimeUnit.SECONDS); timer.forEach(System.out::println); interval.forEach(System.out::println); Output: Process finished with exit code 0
  • 38. Time series Observable<Long> timer = Observable.timer(2, TimeUnit.SECONDS); Observable<Long> interval = Observable.interval(1, TimeUnit.SECONDS); timer.forEach(i -> System.out.println(currentThread().getName() + " - " + i)); interval.forEach(i -> System.out.println(currentThread().getName() + " - " + i)); Output: RxComputationThreadPool-2 - 0 RxComputationThreadPool-1 - 0 RxComputationThreadPool-2 - 1 Process finished with exit code 0 Thread.sleep(2000);
  • 39. Schedulers public final class Schedulers { public static Scheduler immediate() {...} public static Scheduler trampoline() {...} public static Scheduler newThread() {...} public static Scheduler computation() {...} public static Scheduler io() {...} public static TestScheduler test() {...} public static Scheduler from(Executor executor) {...} }
  • 40. Schedulers Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(/* an Observer */);
  • 42. Merge Observable<Integer> odds = Observable .just(1, 3, 5).subscribeOn(Schedulers.io()); Observable<Integer> evens = Observable.just(2, 4, 6); Observable.merge(odds, evens) .subscribe( System.out::println, System.err::println, () -> System.out.println("Finished"));
  • 44. Zip Observable<String> odds = Observable.just("A", "B", "C", "D"); Observable<Integer> evens = Observable.just(2, 4, 6); Observable.zip(odds, evens, (a, b) -> a + "-" + b) .subscribe( System.out::println, System.err::println, () -> System.out.println("Finished")); Output: A-2 B-4 C-6 Finished
  • 45. Cold & Hot Observables ● Cold Observable emits events only having subscriber ● Hot Observable emits events even without subscribers
  • 47. Backpressure Backpreasure is a way to slow down emission of elements It can act on observing side Strategies: ● Buffering items (buffer, window) ● Skipping items (sampling, throttling, etc) ● Request for specific number of elements
  • 48. Backpressure: request for elements public interface Producer { void request(long n); }
  • 49. Stream API vs RxJava RxJava: ● allows to process data in chosen thread, this is useful for IO, computations, specialized threads (GUI threads), ● allows synchronization on clocks and application events, ● works in push mode, producer initiates data transfer, but consumer may control data flow via backpressure. Stream API: ● tuned for hot data processing, ● good for parallel computation, ● has rich set of collectors for data.
  • 50. Shakespeare Plays Scrabble Benchmark (throughput) Non-Parallel Streams 44.995 ± 1.718 ms/op Parallel Streams 14.095 ± 0.616 ms/op RxJava 310.378 ± 9.688 ms/op RxJava (2.0.0-RC4) 156.334 ± 8.423 ms/op Performance Based on JMH benchmark by Jose Paumard
  • 51. Scenarios where RxJava shines ● Observables are better callbacks (easily wrap callbacks) ● Observables are highly composable (on contrast with callbacks) ● Provide async stream of data (on contrast with CompletableFuture) ● Observables can handle errors (have retry / backup strategies) ● Give complete control over running threads ● Good for managing IO rich application workflows ● Perfect for Android development (no Java 8 required, retrolambda compatible) ● Netflix uses RxJava for most their internal APIs
  • 53. Stream API and RxJava are friends! You can easily build Observable from Stream Iterator<T> iterator = ...; Observable<T> observable = Observable.from(() -> iterator); You can map Observable to Stream by implementing adapter public static<T> Iterator<T> of(Observable<? extends T> ob){ class Adapter implements Iterator<T>, Consumer<T> { ... } return new Adapter(); }
  • 54. External libraries that work with RxJava ● hystrix - latency and fault tolerance bulkheading library ● camel RX - to reuse Apache Camel components ● rxjava-http-tail - allows you to follow logs over HTTP ● mod-rxvertx - extension for VertX that provides support Rx ● rxjava-jdbc - use RxJava with JDBC to stream ResultSets ● rtree - immutable in-memory R-tree and R*-tree with RxJava ● and many more ...
  • 55. RxJava is not new JDeferred CompletableFuture<T> Scala.Rx Scala Actors Spring Integration
  • 56. What’s around? ● Google Agera - reactive streams for Android by Google ● Project Reactor - flow API implementation by Pivotal & Spring ● Akka-Streams - Akka based streams ● Monix - Scala based implementation of Reactive Streams ● Vert.x, Lagom - if you want more than streams
  • 57. Let’s build something with RxJava
  • 58. Use case: Stream of tweets Created with Balsamiq Mockups
  • 59. Twitter API Twitter Stream API (WebSocket alike): ● Doc: https://dev.twitter.com/streaming/overview ● Library: com.twitter:hbc-core:2.2.0 Twitter REST API: ● GET https://api.twitter.com/1.1/users/show.json?screen_name=jeeconf ● GET https://api.twitter.com/1.1/search/tweets.json?q=from:jeeconf
  • 60. Let’s look at entities class Tweet { String text; int favorite_count; String author; int author_followers; } class Profile { String screen_name; String name; String location; int statuses_count; int followers_count; } class UserWithTweet { Profile profile; Tweet tweet; }
  • 62. Profile getUserProfile(String screenName) { ObjectMapper om = new ObjectMapper(); return (Profile) om.readValue(om.readTree( Unirest.get(API_BASE_URL + "users/show.json") .queryString("screen_name", screenName) .header("Authorization", bearerAuth(authToken.get())) .asString() .getBody()), Profile.class); } Get user profile synchronously
  • 63. Get user profile asynchronously Observable<Profile> getUserProfile(String screenName) { return Observable.fromCallable(() -> { ObjectMapper om = new ObjectMapper(); return (Profile) om.readValue(om.readTree( Unirest.get(API_BASE_URL + "users/show.json") .queryString("screen_name", screenName) .header("Authorization", bearerAuth(authToken.get())) .asString() .getBody()), Profile.class); }); }
  • 64. Add some errors handling Observable<Profile> getUserProfile(String screenName) { if (authToken.isPresent()) { return Observable.fromCallable(() -> { ObjectMapper om = new ObjectMapper(); return (Profile) om.readValue(om.readTree( Unirest.get(API_BASE_URL + "users/show.json") .queryString("screen_name", screenName) .header("Authorization", bearerAuth(authToken.get())) .asString() .getBody()), Profile.class); }).doOnCompleted(() -> log("getUserProfile completed for: " + screenName)); } else { return Observable.error(new RuntimeException("Can not connect to twitter")); } }
  • 65. Let’s do something concurrently Illustration: Arsenal Firearms S.r.l.
  • 67. Get data concurrently Observable<UserWithTweet> getUserAndPopularTweet(String author){ return Observable.just(author) .flatMap(u -> { Observable<Profile> profile = client.getUserProfile(u) .subscribeOn(Schedulers.io()); Observable<Tweet> tweet = client.getUserRecentTweets(u) .defaultIfEmpty(null) .reduce((t1, t2) -> t1.retweet_count > t2.retweet_count ? t1 : t2) .subscribeOn(Schedulers.io()); return Observable.zip(profile, tweet, UserWithTweet::new); }); }
  • 69. Let’s subscribe on stream of tweets!
  • 72. streamClient.getStream("RxJava", "JavaDay", "Java", "Trump", "Hillary") .distinctUntilChanged() .map(p -> p.author) .flatMap(name -> getUserAndPopularTweet(name)) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.immediate()) .subscribe(p -> log.info("The most popular tweet of user " + p.profile.name + ": " + p.tweet)); .scan((u1, u2) -> u1.author_followers > u2.author_followers ? u1 : u2)
  • 73. streamClient.getStream("RxJava", "JavaDay", "Java", "Trump") .scan((u1, u2) -> u1.author_followers > u2.author_followers ? u1 : u2) .distinctUntilChanged() .map(p -> p.author) .flatMap(name -> { Observable<Profile> profile = client.getUserProfile(name) .subscribeOn(Schedulers.io()); Observable<Tweet> tweet = client.getUserRecentTweets(name) .defaultIfEmpty(null) .reduce((t1, t2) -> t1.retweet_count > t2.retweet_count ? t1 : t2) .subscribeOn(Schedulers.io()); return Observable.zip(profile, tweet, UserWithTweet::new); }) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.immediate()) .subscribe(p -> log.info("The most popular tweet of user " + p.profile.name + ": " + p.tweet));
  • 75. @Test public void correctlyJoinsHttpResults() throws Exception { String testUser = "testUser"; Profile profile = new Profile("u1", "Name", "USA", 10, 20, 30); Tweet tweet1 = new Tweet("text-1", 10, 20, testUser, 30); Tweet tweet2 = new Tweet("text-2", 40, 50, testUser, 30); TwitterClient client = mock(TwitterClient.class); when(client.getUserProfile(testUser)).thenReturn(Observable.just(profile)); when(client.getUserRecentTweets(testUser)).thenReturn(Observable.just(tweet1, tweet2)); TestSubscriber<UserWithTweet> testSubscriber = new TestSubscriber<>(); new Solutions().getUserAndPopularTweet(client, testUser).subscribe(testSubscriber); testSubscriber.awaitTerminalEvent(); assertEquals(singletonList(new UserWithTweet(profile, tweet2)), testSubscriber.getOnNextEvents()); }
  • 77. ● API is big (150+ methods to remember) ● Requires to understand underlying magic ● Hard to debug ● Don’t forget about back pressure Conclusions: pitfalls
  • 78. ● It is functional, it is reactive* ● Good for integration scenarios ● Allows to control execution threads ● Easy to compose workflows ● Easy to integrate into existing solutions ● Easy Ok to test * RxJava is inspired by FRP (Functional Reactive Programming), but doesn’t implement it Conclusions: strength
  • 79. ● RxJava 2.0 in few weeks! ● RxJava 2.0 is better and faster ● RxJava 2.0 is Java 9 Flow API compatible ● We will see more and more reactive streams Conclusions: future