hello) {
24 | return Observable.error(new RuntimeException("Not Implemented"));
25 | }
26 |
27 | /**
28 | * Given a stream of numbers, choose the even ones and return a stream like:
29 | *
30 | * 2-Even
31 | * 4-Even
32 | * 6-Even
33 | */
34 | public Observable exerciseFilterMap(Observable nums) {
35 | return Observable.error(new RuntimeException("Not Implemented"));
36 | }
37 |
38 | /**
39 | * Flatten out all video in the stream of Movies into a stream of videoIDs
40 | *
41 | * @param movieLists
42 | * @return Observable of Integers of Movies.videos.id
43 | */
44 | public Observable exerciseConcatMap(Observable movies) {
45 | return Observable.error(new RuntimeException("Not Implemented"));
46 | }
47 |
48 | /**
49 | * Flatten out all video in the stream of Movies into a stream of videoIDs
50 | *
51 | * Use flatMap this time instead of concatMap. In Observable streams
52 | * it is almost always flatMap that is wanted, not concatMap as flatMap
53 | * uses merge instead of concat and allows multiple concurrent streams
54 | * whereas concat only does one at a time.
55 | *
56 | * We'll see more about this later when we add concurrency.
57 | *
58 | * @param movieLists
59 | * @return Observable of Integers of Movies.videos.id
60 | */
61 | public Observable exerciseFlatMap(Observable movies) {
62 | return Observable.error(new RuntimeException("Not Implemented"));
63 | }
64 |
65 | /**
66 | * Retrieve the largest number.
67 | *
68 | * Use reduce to select the maximum value in a list of numbers.
69 | */
70 | public Observable exerciseReduce(Observable nums) {
71 | return Observable.error(new RuntimeException("Not Implemented"));
72 | }
73 |
74 | /**
75 | * Retrieve the id, title, and smallest box art url for every video.
76 | *
77 | * Now let's try combining reduce() with our other functions to build more complex queries.
78 | *
79 | * This is a variation of the problem we solved earlier, where we retrieved the url of the boxart with a
80 | * width of 150px. This time we'll use reduce() instead of filter() to retrieve the _smallest_ box art in
81 | * the boxarts list.
82 | *
83 | * See Exercise 19 of ComposableListExercises
84 | */
85 | public Observable exerciseMovie(Observable movies) {
86 | return Observable.error(new RuntimeException("Not Implemented"));
87 | }
88 |
89 | /**
90 | * Combine 2 streams into pairs using zip.
91 | *
92 | * a -> "one", "two", "red", "blue"
93 | * b -> "fish", "fish", "fish", "fish"
94 | * output -> "one fish", "two fish", "red fish", "blue fish"
95 | */
96 | public Observable exerciseZip(Observable a, Observable b) {
97 | return Observable.error(new RuntimeException("Not Implemented"));
98 | }
99 |
100 | /**
101 | * Don't modify any values in the stream but do handle the error
102 | * and replace it with "default-value".
103 | */
104 | public Observable handleError(Observable data) {
105 | return Observable.error(new RuntimeException("Not Implemented"));
106 | }
107 |
108 | /**
109 | * The data stream fails intermittently so return the stream
110 | * with retry capability.
111 | */
112 | public Observable retry(Observable data) {
113 | return Observable.error(new RuntimeException("Not Implemented"));
114 | }
115 |
116 | // This function can be used to build JSON objects within an expression
117 | private static JSON json(Object... keyOrValue) {
118 | JSON json = new JSON();
119 |
120 | for (int counter = 0; counter < keyOrValue.length; counter += 2) {
121 | json.put((String) keyOrValue[counter], keyOrValue[counter + 1]);
122 | }
123 |
124 | return json;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/ObservableSolutions.java:
--------------------------------------------------------------------------------
1 | package learnrxjava;
2 |
3 | import learnrxjava.types.JSON;
4 | import learnrxjava.types.Movies;
5 | import rx.Observable;
6 |
7 | public class ObservableSolutions extends ObservableExercises {
8 |
9 | /**
10 | * Return an Observable that emits a single value "Hello World"
11 | *
12 | * @return "Hello World!"
13 | */
14 | public Observable exerciseHello() {
15 | return Observable.just("Hello World!");
16 | }
17 |
18 | /**
19 | * Transform the incoming Observable from "Hello" to "Hello [Name]" where [Name] is your name.
20 | *
21 | * @param "Hello Name!"
22 | */
23 | public Observable exerciseMap(Observable hello) {
24 | return hello.map(t -> t + " Ben!");
25 | }
26 |
27 | /**
28 | * Given a stream of numbers, choose the even ones and return a stream like:
29 | *
30 | * 2-Even
31 | * 4-Even
32 | * 6-Even
33 | */
34 | public Observable exerciseFilterMap(Observable nums) {
35 | return nums.filter(i -> i % 2 == 0).map(i -> i + "-Even");
36 | }
37 |
38 | /**
39 | * Flatten out all video in the stream of Movies into a stream of videoIDs
40 | *
41 | * @param movieLists
42 | * @return Observable of Integers of Movies.videos.id
43 | */
44 | public Observable exerciseConcatMap(Observable movies) {
45 | return movies. concatMap(ml -> {
46 | return ml.videos.map(v -> v.id);
47 | });
48 | }
49 |
50 | /**
51 | * Flatten out all video in the stream of Movies into a stream of videoIDs
52 | *
53 | * Use flatMap this time instead of concatMap. In Observable streams
54 | * it is almost always flatMap that is wanted, not concatMap as flatMap
55 | * uses merge instead of concat and allows multiple concurrent streams
56 | * whereas concat only does one at a time.
57 | *
58 | * We'll see more about this later when we add concurrency.
59 | *
60 | * @param movieLists
61 | * @return Observable of Integers of Movies.videos.id
62 | */
63 | public Observable exerciseFlatMap(Observable movies) {
64 | return movies. flatMap(ml -> {
65 | return ml.videos.map(v -> v.id);
66 | });
67 | }
68 |
69 | /**
70 | * Retrieve the largest number.
71 | *
72 | * Use reduce to select the maximum value in a list of numbers.
73 | */
74 | public Observable exerciseReduce(Observable nums) {
75 | return nums.reduce((max, item) -> {
76 | if (item > max) {
77 | return item;
78 | } else {
79 | return max;
80 | }
81 | });
82 | }
83 |
84 | /**
85 | * Retrieve the id, title, and smallest box art url for every video.
86 | *
87 | * Now let's try combining reduce() with our other functions to build more complex queries.
88 | *
89 | * This is a variation of the problem we solved earlier, where we retrieved the url of the boxart with a
90 | * width of 150px. This time we'll use reduce() instead of filter() to retrieve the _smallest_ box art in
91 | * the boxarts list.
92 | *
93 | * See Exercise 19 of ComposableListExercises
94 | */
95 | public Observable exerciseMovie(Observable movies) {
96 | return movies.flatMap(ml -> {
97 | return ml.videos. flatMap(v -> {
98 | return v.boxarts.reduce((max, box) -> {
99 | int maxSize = max.height * max.width;
100 | int boxSize = box.height * box.width;
101 | if (boxSize < maxSize) {
102 | return box;
103 | } else {
104 | return max;
105 | }
106 | }).map(maxBoxart -> {
107 | return json("id", v.id, "title", v.title, "boxart", maxBoxart.url);
108 | });
109 | });
110 | });
111 | }
112 |
113 | /**
114 | * Combine 2 streams into pairs using zip.
115 | *
116 | * a -> "one", "two", "red", "blue"
117 | * b -> "fish", "fish", "fish", "fish"
118 | * output -> "one fish", "two fish", "red fish", "blue fish"
119 | */
120 | public Observable exerciseZip(Observable a, Observable b) {
121 | return Observable.zip(a, b, (x, y) -> x + " " + y);
122 | }
123 |
124 | /**
125 | * Don't modify any values in the stream but do handle the error
126 | * and replace it with "default-value".
127 | */
128 | public Observable handleError(Observable data) {
129 | return data.onErrorResumeNext(Observable.just("default-value"));
130 | }
131 |
132 | /**
133 | * The data stream fails intermittently so return the stream
134 | * with retry capability.
135 | */
136 | public Observable retry(Observable data) {
137 | return data.retry();
138 | }
139 |
140 | // This function can be used to build JSON objects within an expression
141 | private static JSON json(Object... keyOrValue) {
142 | JSON json = new JSON();
143 |
144 | for (int counter = 0; counter < keyOrValue.length; counter += 2) {
145 | json.put((String) keyOrValue[counter], keyOrValue[counter + 1]);
146 | }
147 |
148 | return json;
149 | }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/ConditionalRetry.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 |
3 | import java.util.concurrent.atomic.AtomicInteger;
4 |
5 | import rx.Observable;
6 | import rx.Subscriber;
7 |
8 | public class ConditionalRetry {
9 |
10 | public static void main(String[] args) {
11 |
12 | final AtomicInteger c = new AtomicInteger();
13 | Observable oWithRuntimeException = Observable.create((Subscriber super String> s) -> {
14 | System.out.println("Execution: " + c.get());
15 | if (c.incrementAndGet() < 3) {
16 | s.onError(new RuntimeException("retryable"));
17 | } else {
18 | s.onNext("hello");
19 | s.onCompleted();
20 | }
21 | });
22 |
23 | final AtomicInteger c2 = new AtomicInteger();
24 | Observable oWithIllegalStateException = Observable.create((Subscriber super String> s) -> {
25 | System.out.println("Execution: " + c2.get());
26 | if (c2.incrementAndGet() < 3) {
27 | s.onError(new RuntimeException("retryable"));
28 | } else {
29 | s.onError(new IllegalStateException());
30 | }
31 | });
32 |
33 | subscribe(oWithRuntimeException);
34 | subscribe(oWithIllegalStateException);
35 | }
36 |
37 | public static void subscribe(Observable o) {
38 | o = o.materialize().flatMap(n -> {
39 | if (n.isOnError()) {
40 | if (n.getThrowable() instanceof IllegalStateException) {
41 | return Observable.just(n);
42 | } else {
43 | return Observable.error(n.getThrowable());
44 | }
45 | } else {
46 | return Observable.just(n);
47 | }
48 | }).retry().dematerialize();
49 |
50 | o.subscribe(System.out::println, t -> t.printStackTrace());
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/ErrorHandlingBasics.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 |
3 | import rx.Observable;
4 |
5 | public class ErrorHandlingBasics {
6 |
7 | public static void main(String... args) {
8 |
9 | /*
10 | * Errors should be emitted via onError
11 | */
12 | Observable.create(s -> {
13 | s.onError(new RuntimeException("failed"));
14 | }).subscribe(System.out::println, t -> System.out.println("1) Error: " + t));
15 |
16 | /*
17 | * But RxJava trys to do the right thing if an error is thrown
18 | */
19 | Observable.create(s -> {
20 | throw new RuntimeException("failed");
21 | }).subscribe(System.out::println, t -> System.out.println("2) Error: " + t));
22 |
23 | Observable.just("hello").map(s -> {
24 | throw new RuntimeException("failed");
25 | }).subscribe(System.out::println, t -> System.out.println("3) Error: " + t));
26 |
27 | /*
28 | * Conditionals that may return an error can be done in a flatMap
29 | */
30 | Observable.just(true).flatMap(v -> {
31 | if (v) {
32 | return Observable.error(new RuntimeException("failed"));
33 | } else {
34 | return Observable.just("data", "here");
35 | }
36 | }).subscribe(System.out::println, t -> System.out.println("4) Error: " + t));
37 |
38 | /*
39 | * Errors can be handled on any Observable
40 | */
41 | Observable.error(new RuntimeException("failed"))
42 | .onErrorResumeNext(Observable.just("5) data"))
43 | .subscribe(System.out::println, t -> System.out.println("5) Error: " + t));
44 |
45 | /*
46 | * Or the Throwable can be obtained to do conditional logic
47 | */
48 | Observable.error(new IllegalStateException("failed"))
49 | .onErrorResumeNext(t -> {
50 | if (t instanceof IllegalStateException) {
51 | return Observable.error(t);
52 | } else {
53 | return Observable.just("6) data");
54 | }
55 | })
56 | .subscribe(System.out::println, t -> System.out.println("6) Error: " + t));
57 |
58 | Observable.error(new RuntimeException("failed"))
59 | .onErrorResumeNext(t -> {
60 | if (t instanceof IllegalStateException) {
61 | return Observable.error(t);
62 | } else {
63 | return Observable.just("7) data");
64 | }
65 | })
66 | .subscribe(System.out::println, t -> System.out.println("7) Error: " + t));
67 |
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/ErrorHandlingRetryWithBackoff.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 |
3 | import java.util.concurrent.TimeUnit;
4 |
5 | import rx.Observable;
6 |
7 | public class ErrorHandlingRetryWithBackoff {
8 |
9 | public static void main(String... args) {
10 |
11 | /*
12 | * retry(n) can be used to immediately retry n times
13 | */
14 | Observable.create(s -> {
15 | System.out.println("1) subscribing");
16 | s.onError(new RuntimeException("1) always fails"));
17 | }).retry(3).subscribe(System.out::println, t -> System.out.println("1) Error: " + t));
18 |
19 | System.out.println("");
20 |
21 | /*
22 | * retryWhen allows custom behavior on when and if a retry should be done
23 | */
24 | Observable.create(s -> {
25 | System.out.println("2) subscribing");
26 | s.onError(new RuntimeException("2) always fails"));
27 | }).retryWhen(attempts -> {
28 | return attempts.zipWith(Observable.range(1, 3), (n, i) -> i).flatMap(i -> {
29 | System.out.println("2) delay retry by " + i + " second(s)");
30 | return Observable.timer(i, TimeUnit.SECONDS);
31 | }).concatWith(Observable.error(new RuntimeException("Failed after 3 retries")));
32 | }).toBlocking().forEach(System.out::println);
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/FlowControlDebounceBuffer.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 | import java.util.List;
3 | import java.util.concurrent.TimeUnit;
4 |
5 | import rx.Observable;
6 | import rx.Subscriber;
7 | import rx.schedulers.Schedulers;
8 |
9 | public class FlowControlDebounceBuffer {
10 |
11 | public static void main(String args[]) {
12 | // debounce to the last value in each burst
13 | //intermittentBursts().debounce(10, TimeUnit.MILLISECONDS).toBlocking().forEach(System.out::println);
14 |
15 | /* The following will emit a buffered list as it is debounced */
16 | // first we multicast the stream ... using refCount so it handles the subscribe/unsubscribe
17 | Observable burstStream = intermittentBursts().take(20).publish().refCount();
18 | // then we get the debounced version
19 | Observable debounced = burstStream.debounce(10, TimeUnit.MILLISECONDS);
20 | // then the buffered one that uses the debounced stream to demark window start/stop
21 | Observable> buffered = burstStream.buffer(debounced);
22 | // then we subscribe to the buffered stream so it does what we want
23 | buffered.toBlocking().forEach(System.out::println);
24 | }
25 |
26 | /**
27 | * This is an artificial source to demonstrate an infinite stream that bursts intermittently
28 | */
29 | public static Observable intermittentBursts() {
30 | return Observable.create((Subscriber super Integer> s) -> {
31 | while (!s.isUnsubscribed()) {
32 | // burst some number of items
33 | for (int i = 0; i < Math.random() * 20; i++) {
34 | s.onNext(i);
35 | }
36 | try {
37 | // sleep for a random amount of time
38 | // NOTE: Only using Thread.sleep here as an artificial demo.
39 | Thread.sleep((long) (Math.random() * 1000));
40 | } catch (Exception e) {
41 | // do nothing
42 | }
43 | }
44 | }).subscribeOn(Schedulers.newThread()); // use newThread since we are using sleep to block
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/FlowControlReactivePullCold.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 | import java.util.ArrayList;
3 | import java.util.Iterator;
4 | import java.util.concurrent.atomic.AtomicLong;
5 |
6 | import rx.Observable;
7 | import rx.Subscriber;
8 | import rx.schedulers.Schedulers;
9 |
10 | /**
11 | * Example of a "cold Observable" using "reactive pull" to emit only as many items as requested by Subscriber.
12 | */
13 | public class FlowControlReactivePullCold {
14 |
15 | public static void main(String[] args) {
16 | getData(1).observeOn(Schedulers.computation()).toBlocking().forEach(System.out::println);
17 | }
18 |
19 | /**
20 | * This is a simple example of an Observable Iterable using "reactive pull".
21 | */
22 | public static Observable getData(int id) {
23 | // simulate a finite, cold data source
24 | final ArrayList data = new ArrayList();
25 | for (int i = 0; i < 5000; i++) {
26 | data.add(i + id);
27 | }
28 | return fromIterable(data);
29 | }
30 |
31 | /**
32 | * A more performant but more complicated implementation can be seen at:
33 | * https://github.com/Netflix/RxJava/blob/master/rxjava-core/src/main/java/rx/internal/operators/OnSubscribeFromIterable.java
34 | *
35 | * Real code should just use Observable.from(Iterable iter) instead of re-implementing this logic.
36 | *
37 | * This is being shown as a simplified version to demonstrate how "reactive pull" data sources are implemented.
38 | */
39 | public static Observable fromIterable(Iterable it) {
40 | // return as Observable (real code would likely do IO of some kind)
41 | return Observable.create((Subscriber super Integer> s) -> {
42 | final Iterator iter = it.iterator();
43 | final AtomicLong requested = new AtomicLong();
44 | s.setProducer((long request) -> {
45 | /*
46 | * We add the request but only kick off work if at 0.
47 | *
48 | * This is done because over async boundaries `request(n)` can be called multiple times by
49 | * another thread while this `Producer` is still emitting. We only want one thread ever emitting.
50 | */
51 | if (requested.getAndAdd(request) == 0) {
52 | do {
53 | if(s.isUnsubscribed()) {
54 | return;
55 | }
56 | if (iter.hasNext()) {
57 | s.onNext(iter.next());
58 | } else {
59 | s.onCompleted();
60 | }
61 | } while (requested.decrementAndGet() > 0);
62 | }
63 | });
64 | });
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/FlowControlSampleExample.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 | import java.util.concurrent.TimeUnit;
3 |
4 | import rx.Observable;
5 | import rx.Subscriber;
6 | import rx.schedulers.Schedulers;
7 |
8 | public class FlowControlSampleExample {
9 |
10 | public static void main(String args[]) {
11 | hotStream().sample(500, TimeUnit.MILLISECONDS).toBlocking().forEach(System.out::println);
12 | }
13 |
14 | /**
15 | * This is an artificial source to demonstrate an infinite stream that emits randomly
16 | */
17 | public static Observable hotStream() {
18 | return Observable.create((Subscriber super Integer> s) -> {
19 | int i = 0;
20 | while (!s.isUnsubscribed()) {
21 | s.onNext(i++);
22 | try {
23 | // sleep for a random amount of time
24 | // NOTE: Only using Thread.sleep here as an artificial demo.
25 | Thread.sleep((long) (Math.random() * 100));
26 | } catch (Exception e) {
27 | // do nothing
28 | }
29 | }
30 | }).subscribeOn(Schedulers.newThread());
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/FlowControlThrottleExample.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 | import java.util.concurrent.TimeUnit;
3 |
4 | import rx.Observable;
5 | import rx.Subscriber;
6 | import rx.schedulers.Schedulers;
7 |
8 | public class FlowControlThrottleExample {
9 |
10 | public static void main(String args[]) {
11 | // first item emitted in each time window
12 | hotStream().throttleFirst(500, TimeUnit.MILLISECONDS).take(10).toBlocking().forEach(System.out::println);
13 |
14 | // last item emitted in each time window
15 | hotStream().throttleLast(500, TimeUnit.MILLISECONDS).take(10).toBlocking().forEach(System.out::println);
16 | }
17 |
18 | /**
19 | * This is an artificial source to demonstrate an infinite stream that emits randomly
20 | */
21 | public static Observable hotStream() {
22 | return Observable.create((Subscriber super Integer> s) -> {
23 | int i = 0;
24 | while (!s.isUnsubscribed()) {
25 | s.onNext(i++);
26 | try {
27 | // sleep for a random amount of time
28 | // NOTE: Only using Thread.sleep here as an artificial demo.
29 | Thread.sleep((long) (Math.random() * 100));
30 | } catch (Exception e) {
31 | // do nothing
32 | }
33 | }
34 | }).subscribeOn(Schedulers.newThread());
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/FlowControlWindowExample.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 |
3 | import java.util.concurrent.TimeUnit;
4 |
5 | import rx.Observable;
6 | import rx.Subscriber;
7 | import rx.schedulers.Schedulers;
8 |
9 | public class FlowControlWindowExample {
10 |
11 | public static void main(String args[]) {
12 | // buffer every 500ms (using 999999999 to mark start of output)
13 | hotStream().window(500, TimeUnit.MILLISECONDS).take(10).flatMap(w -> w.startWith(999999999)).toBlocking().forEach(System.out::println);
14 |
15 | // buffer 10 items at a time (using 999999999 to mark start of output)
16 | hotStream().window(10).take(2).flatMap(w -> w.startWith(999999999)).toBlocking().forEach(System.out::println);
17 |
18 | System.out.println("Done");
19 | }
20 |
21 | /**
22 | * This is an artificial source to demonstrate an infinite stream that bursts intermittently
23 | */
24 | public static Observable hotStream() {
25 | return Observable.create((Subscriber super Integer> s) -> {
26 | while (!s.isUnsubscribed()) {
27 | // burst some number of items
28 | for (int i = 0; i < Math.random() * 20; i++) {
29 | s.onNext(i);
30 | }
31 | try {
32 | // sleep for a random amount of time
33 | // NOTE: Only using Thread.sleep here as an artificial demo.
34 | Thread.sleep((long) (Math.random() * 1000));
35 | } catch (Exception e) {
36 | // do nothing
37 | }
38 | }
39 | }).subscribeOn(Schedulers.newThread()); // use newThread since we are using sleep to block
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/GroupByExamples.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 | import java.util.Arrays;
3 | import java.util.concurrent.TimeUnit;
4 |
5 | import rx.Observable;
6 |
7 | public class GroupByExamples {
8 |
9 | public static void main(String args[]) {
10 | // odd/even into 2 lists
11 | Observable.range(1, 100)
12 | .groupBy(n -> n % 2 == 0)
13 | .flatMap(g -> {
14 | return g.toList();
15 | }).forEach(System.out::println);
16 |
17 | System.out.println("2--------------------------------------------------------------------------------------------------------");
18 |
19 | // odd/even into lists of 10
20 | Observable.range(1, 100)
21 | .groupBy(n -> n % 2 == 0)
22 | .flatMap(g -> {
23 | return g.take(10).toList();
24 | }).forEach(System.out::println);
25 |
26 | System.out.println("3--------------------------------------------------------------------------------------------------------");
27 |
28 | //odd/even into lists of 10
29 | Observable.range(1, 100)
30 | .groupBy(n -> n % 2 == 0)
31 | .flatMap(g -> {
32 | return g.filter(i -> i <= 20).toList();
33 | }).forEach(System.out::println);
34 |
35 | System.out.println("4--------------------------------------------------------------------------------------------------------");
36 |
37 | //odd/even into lists of 20 but only take the first 2 groups
38 | Observable.range(1, 100)
39 | .groupBy(n -> n % 2 == 0)
40 | .flatMap(g -> {
41 | return g.take(20).toList();
42 | }).take(2).forEach(System.out::println);
43 |
44 | System.out.println("5--------------------------------------------------------------------------------------------------------");
45 |
46 | //odd/even into 2 lists with numbers less than 30
47 | Observable.range(1, 100)
48 | .groupBy(n -> n % 2 == 0)
49 | .flatMap(g -> {
50 | return g.takeWhile(i -> i < 30).toList();
51 | }).filter(l -> !l.isEmpty()).forEach(System.out::println);
52 |
53 | System.out.println("6--------------------------------------------------------------------------------------------------------");
54 |
55 | Observable.from(Arrays.asList("a", "b", "c", "a", "b", "c", "a", "b", "c", "a", "b", "c", "a", "b", "c", "a", "b", "c"))
56 | .groupBy(n -> n)
57 | .flatMap(g -> {
58 | return g.take(3).reduce((s, s2) -> s + s2);
59 | }).forEach(System.out::println);
60 |
61 | System.out.println("7--------------------------------------------------------------------------------------------------------");
62 |
63 | Observable.timer(0, 1, TimeUnit.MILLISECONDS)
64 | .groupBy(n -> n % 2 == 0)
65 | .flatMap(g -> {
66 | return g.take(10).toList();
67 | }).take(2).toBlocking().forEach(System.out::println);
68 |
69 | System.out.println("8--------------------------------------------------------------------------------------------------------");
70 |
71 | Observable.timer(0, 1, TimeUnit.MILLISECONDS)
72 | .take(20)
73 | .groupBy(n -> n % 2 == 0)
74 | .flatMap(g -> {
75 | return g.toList();
76 | }).toBlocking().forEach(System.out::println);
77 |
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/GroupByLogic.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 | import java.util.concurrent.TimeUnit;
3 |
4 | import rx.Observable;
5 | import rx.observers.TestSubscriber;
6 | import rx.schedulers.Schedulers;
7 | import rx.schedulers.TestScheduler;
8 | import rx.subjects.PublishSubject;
9 |
10 | public class GroupByLogic {
11 |
12 | public static void main(String[] args) {
13 | final TestScheduler testScheduler = Schedulers.test();
14 | final PublishSubject testSubject = PublishSubject. create();
15 | TestSubscriber ts = new TestSubscriber();
16 |
17 | testSubject.groupBy(playEvt -> playEvt.getOriginatorId())
18 | .flatMap(groupedObservable -> {
19 | System.out.println("***** new group: " + groupedObservable.getKey());
20 | return groupedObservable
21 | /* Timeout after last event, and preventing memory leaks so that inactive streams are closed */
22 | .timeout(3, TimeUnit.HOURS, testScheduler)
23 | /* So that consecutive identical playevents are skipped, can also use skipWhile */
24 | .distinctUntilChanged(PlayEvent::getSession)
25 | .onErrorResumeNext(t -> {
26 | System.out.println(" ***** complete group: " + groupedObservable.getKey());
27 | // complete if we timeout or have an error of any kind (this could emit a special PlayEvent instead
28 | return Observable.empty();
29 | })
30 | // since we have finite groups we can `reduce` to a single value, otherwise use `scan` if you want to emit along the way
31 | .reduce(new StreamState(), (state, playEvent) -> {
32 | System.out.println(" state: " + state + " event: " + playEvent.id + "-" + playEvent.session);
33 | // business logic happens here to accumulate state
34 | state.addEvent(playEvent);
35 | return state;
36 | })
37 | .filter(state -> {
38 | // if using `scan` above instead of `reduce` you could conditionally remove what you don't want to pass down
39 | return true;
40 | });
41 | })
42 | .doOnNext(e -> System.out.println(">>> Output State: " + e))
43 | .subscribe(ts);
44 |
45 | testSubject.onNext(createPlayEvent(1, "a"));
46 | testSubject.onNext(createPlayEvent(2, "a"));
47 | testScheduler.advanceTimeBy(2, TimeUnit.HOURS);
48 |
49 | testSubject.onNext(createPlayEvent(1, "b"));
50 | testScheduler.advanceTimeBy(2, TimeUnit.HOURS);
51 |
52 | testSubject.onNext(createPlayEvent(1, "a"));
53 | testSubject.onNext(createPlayEvent(2, "b"));
54 |
55 | System.out.println("onNext after 4 hours: " + ts.getOnNextEvents());
56 |
57 | testScheduler.advanceTimeBy(3, TimeUnit.HOURS);
58 |
59 | System.out.println("onNext after 7 hours: " + ts.getOnNextEvents());
60 |
61 | testSubject.onCompleted();
62 | testSubject.onNext(createPlayEvent(2, "b"));
63 |
64 | System.out.println("onNext after complete: " + ts.getOnNextEvents());
65 | ts.assertTerminalEvent();
66 | ts.assertNoErrors();
67 | }
68 |
69 | public static PlayEvent createPlayEvent(int id, String v) {
70 | return new PlayEvent(id, v);
71 | }
72 |
73 | public static class PlayEvent {
74 |
75 | private int id;
76 | private String session;
77 |
78 | public PlayEvent(int id, String session) {
79 | this.id = id;
80 | this.session = session;
81 | }
82 |
83 | public int getOriginatorId() {
84 | return id;
85 | }
86 |
87 | public String getSession() {
88 | return session;
89 | }
90 |
91 | }
92 |
93 | public static class StreamState {
94 |
95 | private int id = -1;
96 |
97 | public void addEvent(PlayEvent event) {
98 | if (id == -1) {
99 | this.id = event.id;
100 | }
101 | // add business logic here
102 | }
103 |
104 | @Override
105 | public String toString() {
106 | return "StreamState => id: " + id;
107 | }
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/HelloWorld.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 | import java.util.concurrent.TimeUnit;
3 |
4 | import rx.Observable;
5 | import rx.Observable.OnSubscribe;
6 | import rx.Subscriber;
7 | import rx.schedulers.Schedulers;
8 |
9 | public class HelloWorld {
10 |
11 | public static void main(String[] args) {
12 |
13 | // Hello World
14 | Observable.create(subscriber -> {
15 | subscriber.onNext("Hello World!");
16 | subscriber.onCompleted();
17 | }).subscribe(System.out::println);
18 |
19 | // shorten by using helper method
20 | Observable.just("Hello", "World!")
21 | .subscribe(System.out::println);
22 |
23 | // add onError and onComplete listeners
24 | Observable.just("Hello World!")
25 | .subscribe(System.out::println,
26 | Throwable::printStackTrace,
27 | () -> System.out.println("Done"));
28 |
29 | // expand to show full classes
30 | Observable.create(new OnSubscribe() {
31 |
32 | @Override
33 | public void call(Subscriber super String> subscriber) {
34 | subscriber.onNext("Hello World!");
35 | subscriber.onCompleted();
36 | }
37 |
38 | }).subscribe(new Subscriber() {
39 |
40 | @Override
41 | public void onCompleted() {
42 | System.out.println("Done");
43 | }
44 |
45 | @Override
46 | public void onError(Throwable e) {
47 | e.printStackTrace();
48 | }
49 |
50 | @Override
51 | public void onNext(String t) {
52 | System.out.println(t);
53 | }
54 |
55 | });
56 |
57 | // add error propagation
58 | Observable.create(subscriber -> {
59 | try {
60 | subscriber.onNext("Hello World!");
61 | subscriber.onCompleted();
62 | } catch (Exception e) {
63 | subscriber.onError(e);
64 | }
65 | }).subscribe(System.out::println);
66 |
67 | // add concurrency (manually)
68 | Observable.create(subscriber -> {
69 | new Thread(() -> {
70 | try {
71 | subscriber.onNext(getData());
72 | subscriber.onCompleted();
73 | } catch (Exception e) {
74 | subscriber.onError(e);
75 | }
76 | }).start();
77 | }).subscribe(System.out::println);
78 |
79 | // add concurrency (using a Scheduler)
80 | Observable.create(subscriber -> {
81 | try {
82 | subscriber.onNext(getData());
83 | subscriber.onCompleted();
84 | } catch (Exception e) {
85 | subscriber.onError(e);
86 | }
87 | }).subscribeOn(Schedulers.io())
88 | .subscribe(System.out::println);
89 |
90 | // add operator
91 | Observable.create(subscriber -> {
92 | try {
93 | subscriber.onNext(getData());
94 | subscriber.onCompleted();
95 | } catch (Exception e) {
96 | subscriber.onError(e);
97 | }
98 | }).subscribeOn(Schedulers.io())
99 | .map(data -> data + " --> at " + System.currentTimeMillis())
100 | .subscribe(System.out::println);
101 |
102 | // add error handling
103 | Observable.create(subscriber -> {
104 | try {
105 | subscriber.onNext(getData());
106 | subscriber.onCompleted();
107 | } catch (Exception e) {
108 | subscriber.onError(e);
109 | }
110 | }).subscribeOn(Schedulers.io())
111 | .map(data -> data + " --> at " + System.currentTimeMillis())
112 | .onErrorResumeNext(e -> Observable.just("Fallback Data"))
113 | .subscribe(System.out::println);
114 |
115 | // infinite
116 | Observable.create(subscriber -> {
117 | int i = 0;
118 | while (!subscriber.isUnsubscribed()) {
119 | subscriber.onNext(i++);
120 | }
121 | }).take(10).subscribe(System.out::println);
122 |
123 | //Hello World
124 | Observable.create(subscriber -> {
125 | throw new RuntimeException("failed!");
126 | }).onErrorResumeNext(throwable -> {
127 | return Observable.just("fallback value");
128 | }).subscribe(System.out::println);
129 |
130 | Observable.create(subscriber -> {
131 | throw new RuntimeException("failed!");
132 | }).onErrorResumeNext(Observable.just("fallback value"))
133 | .subscribe(System.out::println);
134 |
135 | Observable.create(subscriber -> {
136 | throw new RuntimeException("failed!");
137 | }).onErrorReturn(throwable -> {
138 | return "fallback value";
139 | }).subscribe(System.out::println);
140 |
141 | Observable.create(subscriber -> {
142 | throw new RuntimeException("failed!");
143 | }).retryWhen(attempts -> {
144 | return attempts.zipWith(Observable.range(1, 3), (throwable, i) -> i)
145 | .flatMap(i -> {
146 | System.out.println("delay retry by " + i + " second(s)");
147 | return Observable.timer(i, TimeUnit.SECONDS);
148 | }).concatWith(Observable.error(new RuntimeException("Exceeded 3 retries")));
149 | })
150 | .subscribe(System.out::println, t -> t.printStackTrace());
151 |
152 | try {
153 | Thread.sleep(20000);
154 | } catch (InterruptedException e1) {
155 | e1.printStackTrace();
156 | }
157 | }
158 |
159 | private static String getData() {
160 | return "Got Data!";
161 | }
162 |
163 | }
164 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/ParallelExecution.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 | import rx.Observable;
3 | import rx.Subscriber;
4 | import rx.schedulers.Schedulers;
5 |
6 | public class ParallelExecution {
7 |
8 | public static void main(String[] args) {
9 | System.out.println("------------ mergingAsync");
10 | mergingAsync();
11 | System.out.println("------------ mergingSync");
12 | mergingSync();
13 | System.out.println("------------ mergingSyncMadeAsync");
14 | mergingSyncMadeAsync();
15 | System.out.println("------------ flatMapExampleSync");
16 | flatMapExampleSync();
17 | System.out.println("------------ flatMapExampleAsync");
18 | flatMapExampleAsync();
19 | System.out.println("------------ flatMapBufferedExampleAsync");
20 | flatMapBufferedExampleAsync();
21 | System.out.println("------------ flatMapWindowedExampleAsync");
22 | flatMapWindowedExampleAsync();
23 | System.out.println("------------");
24 | }
25 |
26 | private static void mergingAsync() {
27 | Observable.merge(getDataAsync(1), getDataAsync(2)).toBlocking().forEach(System.out::println);
28 | }
29 |
30 | private static void mergingSync() {
31 | // here you'll see the delay as each is executed synchronously
32 | Observable.merge(getDataSync(1), getDataSync(2)).toBlocking().forEach(System.out::println);
33 | }
34 |
35 | private static void mergingSyncMadeAsync() {
36 | // if you have something synchronous and want to make it async, you can schedule it like this
37 | // so here we see both executed concurrently
38 | Observable.merge(getDataSync(1).subscribeOn(Schedulers.io()), getDataSync(2).subscribeOn(Schedulers.io())).toBlocking().forEach(System.out::println);
39 | }
40 |
41 | private static void flatMapExampleAsync() {
42 | Observable.range(0, 5).flatMap(i -> {
43 | return getDataAsync(i);
44 | }).toBlocking().forEach(System.out::println);
45 | }
46 |
47 | private static void flatMapExampleSync() {
48 | Observable.range(0, 5).flatMap(i -> {
49 | return getDataSync(i);
50 | }).toBlocking().forEach(System.out::println);
51 | }
52 |
53 | private static void flatMapBufferedExampleAsync() {
54 | Observable.range(0, 5000).buffer(500).flatMap(i -> {
55 | return Observable.from(i).subscribeOn(Schedulers.computation()).map(item -> {
56 | // simulate computational work
57 | try {
58 | Thread.sleep(1);
59 | } catch (Exception e) {
60 | }
61 | return item + " processed " + Thread.currentThread();
62 | });
63 | }).toBlocking().forEach(System.out::println);
64 | }
65 |
66 | private static void flatMapWindowedExampleAsync() {
67 | Observable.range(0, 5000).window(500).flatMap(work -> {
68 | return work.observeOn(Schedulers.computation()).map(item -> {
69 | // simulate computational work
70 | try {
71 | Thread.sleep(1);
72 | } catch (Exception e) {
73 | }
74 | return item + " processed " + Thread.currentThread();
75 | });
76 | }).toBlocking().forEach(System.out::println);
77 | }
78 |
79 | // artificial representations of IO work
80 | static Observable getDataAsync(int i) {
81 | return getDataSync(i).subscribeOn(Schedulers.io());
82 | }
83 |
84 | static Observable getDataSync(int i) {
85 | return Observable.create((Subscriber super Integer> s) -> {
86 | // simulate latency
87 | try {
88 | Thread.sleep(1000);
89 | } catch (Exception e) {
90 | e.printStackTrace();
91 | }
92 | s.onNext(i);
93 | s.onCompleted();
94 | });
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/ParallelExecutionExample.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 | import java.util.List;
3 |
4 | import rx.Observable;
5 | import rx.Subscriber;
6 | import rx.schedulers.Schedulers;
7 |
8 | public class ParallelExecutionExample {
9 |
10 | public static void run() {
11 | Observable searchTile = getSearchResults("search term");
12 |
13 | Observable populatedTiles = searchTile.flatMap(t -> {
14 | Observable reviews = getSellerReviews(t.getSellerId());
15 | Observable imageUrl = getProductImage(t.getProductId());
16 |
17 | return Observable.zip(reviews, imageUrl, (r, u) -> {
18 | return new TileResponse(t, r, u);
19 | });
20 | });
21 |
22 | List allTiles = populatedTiles.toList()
23 | .toBlocking().single();
24 | }
25 |
26 | public static void main(String[] args) {
27 | final long startTime = System.currentTimeMillis();
28 |
29 | Observable searchTile = getSearchResults("search term")
30 | .doOnSubscribe(() -> logTime("Search started ", startTime))
31 | .doOnCompleted(() -> logTime("Search completed ", startTime));
32 |
33 | Observable populatedTiles = searchTile.flatMap(t -> {
34 | Observable reviews = getSellerReviews(t.getSellerId())
35 | .doOnCompleted(() -> logTime("getSellerReviews[" + t.id + "] completed ", startTime));
36 | Observable imageUrl = getProductImage(t.getProductId())
37 | .doOnCompleted(() -> logTime("getProductImage[" + t.id + "] completed ", startTime));
38 |
39 | return Observable.zip(reviews, imageUrl, (r, u) -> {
40 | return new TileResponse(t, r, u);
41 | }).doOnCompleted(() -> logTime("zip[" + t.id + "] completed ", startTime));
42 | });
43 |
44 | List allTiles = populatedTiles.toList()
45 | .doOnCompleted(() -> logTime("All Tiles Completed ", startTime))
46 | .toBlocking().single();
47 | }
48 |
49 | private static Observable getSearchResults(String string) {
50 | return mockClient(new Tile(1), new Tile(2), new Tile(3));
51 | }
52 |
53 | private static Observable getSellerReviews(int id) {
54 | return mockClient(new Reviews());
55 | }
56 |
57 | private static Observable getProductImage(int id) {
58 | return mockClient("image_" + id);
59 | }
60 |
61 | private static void logTime(String message, long startTime) {
62 | System.out.println(message + " => " + (System.currentTimeMillis() - startTime) + "ms");
63 | }
64 |
65 | private static Observable mockClient(T... ts) {
66 | return Observable.create((Subscriber super T> s) -> {
67 | // simulate latency
68 | try {
69 | Thread.sleep(1000);
70 | } catch (Exception e) {
71 | }
72 | for (T t : ts) {
73 | s.onNext(t);
74 | }
75 | s.onCompleted();
76 | }).subscribeOn(Schedulers.io());
77 | // note the use of subscribeOn to make an otherwise synchronous Observable async
78 | }
79 |
80 | public static class TileResponse {
81 |
82 | public TileResponse(Tile t, Reviews r, String u) {
83 | // store the values
84 | }
85 |
86 | }
87 |
88 | public static class Tile {
89 |
90 | private final int id;
91 |
92 | public Tile(int i) {
93 | this.id = i;
94 | }
95 |
96 | public int getSellerId() {
97 | return id;
98 | }
99 |
100 | public int getProductId() {
101 | return id;
102 | }
103 |
104 | }
105 |
106 | public static class Reviews {
107 |
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/ScanVsReduceExample.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 |
3 | import java.util.ArrayList;
4 |
5 | import rx.Observable;
6 |
7 | public class ScanVsReduceExample {
8 |
9 | public static void main(String... args) {
10 | Observable.range(0, 10).reduce(new ArrayList<>(), (list, i) -> {
11 | list.add(i);
12 | return list;
13 | }).forEach(System.out::println);
14 |
15 | System.out.println("... vs ...");
16 |
17 | Observable.range(0, 10).scan(new ArrayList<>(), (list, i) -> {
18 | list.add(i);
19 | return list;
20 | }).forEach(System.out::println);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/UnitTesting.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 |
3 | import java.util.Arrays;
4 | import java.util.concurrent.TimeUnit;
5 |
6 | import rx.Observable;
7 | import rx.observers.TestSubscriber;
8 | import rx.schedulers.Schedulers;
9 | import rx.schedulers.TestScheduler;
10 |
11 | public class UnitTesting {
12 |
13 | public static void main(String... args) {
14 | TestScheduler test = Schedulers.test();
15 | TestSubscriber ts = new TestSubscriber<>();
16 |
17 | Observable.interval(200, TimeUnit.MILLISECONDS, test)
18 | .map(i -> {
19 | return i + " value";
20 | }).subscribe(ts);
21 |
22 | test.advanceTimeBy(200, TimeUnit.MILLISECONDS);
23 | ts.assertReceivedOnNext(Arrays.asList("0 value"));
24 |
25 | test.advanceTimeTo(1000, TimeUnit.MILLISECONDS);
26 | ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value", "2 value", "3 value", "4 value"));
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/learnrxjava/examples/ZipInterval.java:
--------------------------------------------------------------------------------
1 | package learnrxjava.examples;
2 | import java.util.concurrent.TimeUnit;
3 |
4 | import rx.Observable;
5 |
6 |
7 | public class ZipInterval {
8 |
9 | public static void main(String... args) {
10 | Observable