├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ └── res │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── geniusmart │ └── rxjava │ ├── BackpressureOperatorsTest.java │ ├── CombiningOperatorsTest.java │ ├── ConditionalAndBooleanOperatorsTest.java │ ├── ConnectableOperatorsTest.java │ ├── ConvertObservablesTest.java │ ├── CreatingOperatorsTest.java │ ├── CustomOperatorsTest.java │ ├── ErrorHandlingOperatorsTest.java │ ├── FilteringOperatorsTest.java │ ├── MathematicalAndAggregateOperatorsTest.java │ ├── ToOperators.java │ ├── TransformingOperatorsTest.java │ ├── UtilityOperatorsTest.java │ └── utils │ ├── OperatorUtils.java │ └── ThreadTheory.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxJavaOperatorsUTSample 2 | 使用 UT 高效地玩转 RxJava 的操作符 3 | 4 | ##目的 5 | - 在学习 RxJava 的操作符时,尤其是 Android 同学,需要额外写很多代码来实现一个操作符(比如 UI ),比较繁琐。 6 | - 使用 UT ,简单暴力,让我们专注于操作符本身输入输出和处理,事半功倍。 7 | - 使用 UT 将 RxJava 的所有操作符根据官方的弹珠图(marble diagrams),精确的实现输入和输出,对 RxJava 的整个体系将会有更深入了解。 8 | 9 | ##Features 10 | - 使用纯 UT 实现 JxJava 的所有操作符,无需依赖 Android ,也不涉及太多测试技巧,专注于操作符的输入输出和处理 11 | - 有目的性的输入与输出 12 | * 尽可能使用官方操作符的 marble diagrams 实现精确的输入和输出,如 `connect` 、 `replay` 、 `flatMap` 、 `concatMap` 等 13 | * 部分操作符使用 [RxMarbles](http://rxmarbles.com/) 进行实现,如 `combineLatest` 、 `amb` 等 14 | - 对于弹珠图无法完全涵盖知识点的操作符,配备了更多的 UT 和参考文章,如 `repeatWhen` 、 `retryWhen` 、 `defer` 等 15 | 16 | ##预备知识 17 | - 测试线程和 RxJava 操作符所在线程如何顺利的执行完毕 18 | - RxJava 提供的 `TestScheduler` 的用法 19 | - 聚合操作符中的线程如何处理 20 | - 预备知识的相关例子请查看 [ThreadTheory](https://github.com/geniusmart/RxJavaOperatorsUTSample/blob/master/app/src/test/java/com/geniusmart/rxjava/utils/ThreadTheory.java) 21 | 22 | ##Example 23 | 1. 对比并实现 flatMap 和 concatMap 的 marble diagrams 24 | - flatMap 25 | ![flatMap](http://reactivex.io/documentation/operators/images/mergeMap.png) 26 | - concatMap 27 | ![concatMap](http://reactivex.io/documentation/operators/images/concatMap.png) 28 | - 在这两张弹珠图中,输入是完全一样的,但是输出结果不一致,concatMap 变换后保持原有的输入顺序,而flatMap则不然,使用 UT 分别来实现这两张弹珠图: 29 | - flatMap 的 UT 实现: 30 | ```java 31 | Observable.just(1, 2, 3) 32 | .flatMap((Func1>) num -> Observable.interval(num - 1, 33 | TimeUnit.SECONDS, mTestScheduler) 34 | .take(2) 35 | .map(value -> num + "◇")) 36 | .subscribe(mList::add); 37 | 38 | mTestScheduler.advanceTimeBy(100, TimeUnit.SECONDS); 39 | assertEquals(mList, Arrays.asList("1◇", "1◇", "2◇", "3◇", "2◇", "3◇")); 40 | ``` 41 | 42 | - concatMap 的 UT 实现: 43 | ```java 44 | Observable.just(1, 2, 3) 45 | .concatMap((Func1>) num -> Observable.interval(num - 1, 46 | TimeUnit.SECONDS, mTestScheduler) 47 | .take(2) 48 | .map(value -> num + "◇")) 49 | .subscribe(mList::add); 50 | 51 | mTestScheduler.advanceTimeBy(100, TimeUnit.SECONDS); 52 | assertEquals(mList, Arrays.asList("1◇", "1◇", "2◇", "2◇", "3◇", "3◇")); 53 | ``` 54 | 2. TODO 55 | 56 | ##所涵盖的操作符 57 | - Creating Observables 58 | * crate、defer、empty/never/throw、from、interval、just、range、repeat、start、timer 59 | - Transforming Observables 60 | * bufer、flatMap、concatMap、groupBy、map、scan、window 61 | - Filtering Observables 62 | * debounce、distinct、elementAt、filter、first、ignoreElements、last、sample、skip、skipLast、take、takeLast 63 | - Combining Observables 64 | * and/then/when、combineLatest、merge、startWith、switch、zip 65 | - Error Handling Operators 66 | * catch、retry、retryWhen、onErrorReturn、onErrorResumeNext 67 | - Observable Utility Operators 68 | * delay、do、materialize/dematerialize、observeOn、subscribe、timeInterval、timeout、timestamp 69 | - Conditional and Boolean Operators 70 | * all、amb、contains、defaultIfEmpty、sequenceEqual、skipUntil、skipWhile、takeUntil、takeWhile 71 | - Mathematical and Aggregate Operators 72 | * average、concat、count、max、min、reduce、sum 73 | 74 | ##TODO 75 | - Backpressure Operators 76 | - 自定义操作符 77 | - join 78 | - doOnRequest、serialize、using 79 | - Subject 80 | - 《使用 UT 高效地玩转 RxJava 的操作符》 81 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "24.0.0" 6 | defaultConfig { 7 | applicationId "com.geniusmart.rxjava" 8 | minSdkVersion 16 9 | targetSdkVersion 24 10 | versionCode 1 11 | versionName "1.0" 12 | jackOptions { 13 | enabled true 14 | } 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | compileOptions { 23 | targetCompatibility 1.8 24 | sourceCompatibility 1.8 25 | } 26 | } 27 | 28 | dependencies { 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | compile 'com.android.support:appcompat-v7:24.1.1' 31 | compile 'io.reactivex:rxjava:1.2.2' 32 | compile 'io.reactivex:rxjava-math:1.0.0' 33 | compile 'io.reactivex:rxjava-async-util:0.21.0' 34 | compile 'io.reactivex:rxjava-joins:0.22.0' 35 | 36 | testCompile 'junit:junit:4.12' 37 | } 38 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geniusmart/RxJavaOperatorsUTSample/d72d29e9d6f6f93ef82137275f20a43220414723/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geniusmart/RxJavaOperatorsUTSample/d72d29e9d6f6f93ef82137275f20a43220414723/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geniusmart/RxJavaOperatorsUTSample/d72d29e9d6f6f93ef82137275f20a43220414723/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geniusmart/RxJavaOperatorsUTSample/d72d29e9d6f6f93ef82137275f20a43220414723/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geniusmart/RxJavaOperatorsUTSample/d72d29e9d6f6f93ef82137275f20a43220414723/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RxJavaOperatorsUTSample 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/BackpressureOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | /** 4 | * Created by geniusmart on 16/11/6. 5 | *

6 | * strategies for coping with Observables that produce items more rapidly than their observers 7 | * consume them 8 | */ 9 | public class BackpressureOperatorsTest { 10 | 11 | //TODO 结合http://rxmarbles.com/#pausable 12 | } 13 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/CombiningOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | import com.geniusmart.rxjava.utils.OperatorUtils; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import rx.Observable; 14 | import rx.Subscriber; 15 | import rx.functions.Func1; 16 | import rx.functions.Func2; 17 | import rx.joins.Plan0; 18 | import rx.observables.JoinObservable; 19 | import rx.schedulers.Schedulers; 20 | import rx.schedulers.TestScheduler; 21 | 22 | import static junit.framework.Assert.assertEquals; 23 | 24 | /** 25 | * Created by geniusmart on 2016/11/1. 26 | *

27 | * Operators that work with multiple source Observables to create a single Observable 28 | */ 29 | public class CombiningOperatorsTest { 30 | 31 | private TestScheduler mTestScheduler; 32 | private List mList; 33 | 34 | @Before 35 | public void setUp() { 36 | mTestScheduler = new TestScheduler(); 37 | mList = new ArrayList<>(); 38 | } 39 | 40 | /** 41 | * combine sets of items emitted by two or more Observables by means of 42 | * Pattern and Plan intermediaries 43 | * 44 | * @see and/then/when 45 | */ 46 | @Test 47 | public void and_then_when() { 48 | 49 | Observable observable1 = Observable.interval(5, TimeUnit.SECONDS, mTestScheduler) 50 | .skip(1) 51 | .take(5) 52 | .doOnNext(System.out::println); 53 | Observable observable2 = Observable.interval(8, TimeUnit.SECONDS, mTestScheduler) 54 | .take(4) 55 | .map(aLong -> (char) ('A' + aLong)) 56 | .doOnNext(System.out::println); 57 | 58 | Plan0 then = JoinObservable.from(observable1) 59 | .and(observable2) 60 | .then((aLong, aChar) -> aLong + String.valueOf(aChar)); 61 | 62 | JoinObservable.when(then).toObservable().subscribe(mList::add); 63 | 64 | mTestScheduler.advanceTimeBy(50, TimeUnit.SECONDS); 65 | assertEquals(mList, Arrays.asList("1A", "2B", "3C", "4D")); 66 | } 67 | 68 | /** 69 | * when an item is emitted by either of two Observables, combine the latest 70 | * item emitted by each Observable via a specified function and emit items 71 | * based on the results of this function 72 | * 73 | * @see RxMarbles combineLatest diagrams 74 | */ 75 | @Test 76 | public void combineLatest() { 77 | 78 | Observable observable1 = Observable.create(new Observable.OnSubscribe() { 79 | @Override 80 | public void call(Subscriber subscriber) { 81 | OperatorUtils.logThread("observable1"); 82 | subscriber.onNext(1); 83 | OperatorUtils.sleep(500); 84 | subscriber.onNext(2); 85 | OperatorUtils.sleep(1500); 86 | subscriber.onNext(3); 87 | OperatorUtils.sleep(250); 88 | subscriber.onNext(4); 89 | OperatorUtils.sleep(500); 90 | subscriber.onNext(5); 91 | subscriber.onCompleted(); 92 | } 93 | }).subscribeOn(mTestScheduler).doOnNext(System.out::println); 94 | 95 | Observable observable2 = Observable.create(new Observable.OnSubscribe() { 96 | @Override 97 | public void call(Subscriber subscriber) { 98 | OperatorUtils.logThread("observable2"); 99 | OperatorUtils.sleep(250); 100 | subscriber.onNext("A"); 101 | OperatorUtils.sleep(300); 102 | subscriber.onNext("B"); 103 | OperatorUtils.sleep(500); 104 | subscriber.onNext("C"); 105 | OperatorUtils.sleep(100); 106 | subscriber.onNext("D"); 107 | subscriber.onCompleted(); 108 | } 109 | }).subscribeOn(Schedulers.newThread()).doOnNext(System.out::println); 110 | 111 | Observable.combineLatest(observable1, observable2, 112 | (Func2) (integer, s) -> integer + s).subscribe(mList::add); 113 | 114 | //测试线程提前一定时间,让observable1能顺利开始发送数据 115 | mTestScheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS); 116 | System.out.println(mList); 117 | assertEquals(mList, Arrays.asList("1A", "2A", "2B", "2C", "2D", "3D", "4D", "5D")); 118 | } 119 | 120 | //TODO-未完成-join 121 | 122 | /** 123 | * combine items emitted by two Observables whenever an item from one Observable is emitted 124 | * during a time window defined according to an item emitted by the other Observable 125 | * 126 | * @see ReactiveX operators 127 | * documentation: Join 128 | */ 129 | @Test 130 | public void join() { 131 | 132 | Observable o1 = Observable.create(new Observable.OnSubscribe() { 133 | @Override 134 | public void call(Subscriber subscriber) { 135 | OperatorUtils.logThread("observable1"); 136 | subscriber.onNext(1); 137 | OperatorUtils.sleep(500); 138 | subscriber.onNext(2); 139 | OperatorUtils.sleep(100); 140 | subscriber.onNext(3); 141 | OperatorUtils.sleep(500); 142 | subscriber.onNext(4); 143 | OperatorUtils.sleep(500); 144 | subscriber.onCompleted(); 145 | } 146 | }).subscribeOn(mTestScheduler).doOnNext(System.out::println); 147 | 148 | Observable o2 = Observable.create(new Observable.OnSubscribe() { 149 | @Override 150 | public void call(Subscriber subscriber) { 151 | OperatorUtils.logThread("observable2"); 152 | OperatorUtils.sleep(250); 153 | subscriber.onNext("A"); 154 | OperatorUtils.sleep(600); 155 | subscriber.onNext("B"); 156 | OperatorUtils.sleep(200); 157 | subscriber.onNext("C"); 158 | subscriber.onCompleted(); 159 | } 160 | }).subscribeOn(Schedulers.newThread()).doOnNext(System.out::println); 161 | 162 | o1.join(o2, new Func1>() { 163 | @Override 164 | public Observable call(Integer num) { 165 | return Observable.create(new Observable.OnSubscribe() { 166 | @Override 167 | public void call(Subscriber subscriber) { 168 | subscriber.onNext(num); 169 | OperatorUtils.sleep(300); 170 | subscriber.onCompleted(); 171 | } 172 | }).subscribeOn(mTestScheduler); 173 | } 174 | }, new Func1>() { 175 | @Override 176 | public Observable call(String s) { 177 | return Observable.create(new Observable.OnSubscribe() { 178 | @Override 179 | public void call(Subscriber subscriber) { 180 | subscriber.onNext(s); 181 | OperatorUtils.sleep(300); 182 | subscriber.onCompleted(); 183 | } 184 | }).subscribeOn(mTestScheduler); 185 | } 186 | }, new Func2() { 187 | @Override 188 | public Object call(Integer integer, String s) { 189 | return integer + s; 190 | } 191 | }).subscribe(mList::add); 192 | 193 | mTestScheduler.advanceTimeBy(10, TimeUnit.SECONDS); 194 | 195 | System.out.println(mList); 196 | } 197 | 198 | /** 199 | * combine multiple Observables into one by merging their emissions 200 | * 201 | * @see RxMarbles merge 202 | * @see ReactiveX operators 203 | * documentation: Merge 204 | */ 205 | @Test 206 | public void merge() { 207 | Observable observable1 = Observable.interval(5, TimeUnit.SECONDS, mTestScheduler) 208 | .take(5) 209 | .map(aLong -> (aLong + 1) * 20) 210 | .doOnNext(System.out::println); 211 | 212 | Observable observable2 = Observable.interval(18, TimeUnit.SECONDS, mTestScheduler) 213 | .take(2) 214 | .map(aLong -> 1L) 215 | .doOnNext(System.out::println); 216 | 217 | Observable.merge(observable1, observable2).subscribe(mList::add); 218 | 219 | mTestScheduler.advanceTimeBy(1000, TimeUnit.SECONDS); 220 | assertEquals(mList, Arrays.asList(20L, 40L, 60L, 1L, 80L, 100L, 1L)); 221 | } 222 | 223 | /** 224 | * emit a specified sequence of items before beginning to emit the items from the source 225 | * Observable 226 | * 227 | * @see Rxmarbles diagrams startWith 228 | * @see ReactiveX operators 229 | * documentation: StartWith 230 | */ 231 | @Test 232 | public void startWith() { 233 | Observable.just(2, 3).startWith(1).subscribe(System.out::println); 234 | } 235 | 236 | /** 237 | * convert an Observable that emits Observables into a single Observable that emits the items 238 | * emitted by the most-recently-emitted of those Observables 239 | * 240 | * @see ReactiveX operators 241 | * documentation: Switch 242 | */ 243 | @Test 244 | public void switchOnNext() { 245 | Observable o1 = Observable.just(1, 2, 3); 246 | Observable o2 = Observable.just("A", "B", "C"); 247 | 248 | Observable.switchOnNext(Observable.just(o1, o2)).subscribe(mList::add); 249 | System.out.println(mList); 250 | assertEquals(mList, Arrays.asList(1, 2, 3, "A", "B", "C")); 251 | } 252 | 253 | /** 254 | * convert an Observable that emits Observables into a single Observable that emits the items 255 | * emitted by the most-recently-emitted of those Observables 256 | *

257 | * 此例根据官方 marble diagrams 实现 258 | * 259 | * @see switch.png 260 | */ 261 | @Test 262 | public void switchOnNext2() { 263 | 264 | Observable o1 = Observable.interval(0, 1, TimeUnit.SECONDS) 265 | .take(3) 266 | .map(num -> num + 1) 267 | .doOnNext(System.out::println); 268 | 269 | Observable o2 = Observable.interval(0, 1, TimeUnit.SECONDS) 270 | .take(3) 271 | .map(num -> String.valueOf((char) ('A' + num))) 272 | .doOnNext(System.out::println); 273 | 274 | Observable> observable = 275 | Observable.create(new Observable.OnSubscribe>() { 276 | @Override 277 | public void call(Subscriber> subscriber) { 278 | subscriber.onNext(o1); 279 | OperatorUtils.sleep(1500); 280 | subscriber.onNext(o2); 281 | OperatorUtils.sleep(3000); 282 | subscriber.onCompleted(); 283 | } 284 | }); 285 | 286 | Observable.switchOnNext(observable).subscribe(mList::add); 287 | 288 | System.out.println(mList); 289 | assertEquals(mList, Arrays.asList(1L, 2L, "A", "B", "C")); 290 | } 291 | 292 | /** 293 | * @see Rxmarbles diagrams withLatestFrom 294 | * @see ReactiveX 295 | * operators documentation: CombineLatest 296 | */ 297 | @Test 298 | public void withLatestFrom() { 299 | Observable observable1 = Observable.create(new Observable.OnSubscribe() { 300 | @Override 301 | public void call(Subscriber subscriber) { 302 | subscriber.onNext(1); 303 | OperatorUtils.sleep(500); 304 | subscriber.onNext(2); 305 | OperatorUtils.sleep(1500); 306 | subscriber.onNext(3); 307 | OperatorUtils.sleep(250); 308 | subscriber.onNext(4); 309 | OperatorUtils.sleep(500); 310 | subscriber.onNext(5); 311 | } 312 | }).subscribeOn(mTestScheduler).doOnNext(System.out::println); 313 | 314 | Observable observable2 = Observable.create(new Observable.OnSubscribe() { 315 | @Override 316 | public void call(Subscriber subscriber) { 317 | OperatorUtils.sleep(250); 318 | subscriber.onNext("A"); 319 | OperatorUtils.sleep(300); 320 | subscriber.onNext("B"); 321 | OperatorUtils.sleep(500); 322 | subscriber.onNext("C"); 323 | OperatorUtils.sleep(100); 324 | subscriber.onNext("D"); 325 | } 326 | }).subscribeOn(Schedulers.newThread()).doOnNext(System.out::println); 327 | 328 | observable1.withLatestFrom(observable2, (integer, s) -> integer + s).subscribe(mList::add); 329 | 330 | mTestScheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS); 331 | assertEquals(mList, Arrays.asList("2A", "3D", "4D", "5D")); 332 | } 333 | 334 | /** 335 | * combine the emissions of multiple Observables together via a specified function and emit 336 | * single items for each combination based on the results of this function 337 | * 338 | * @see Rxmarbles diagrams zip 339 | * @see ReactiveX operators 340 | * documentation: Zip 341 | */ 342 | @Test 343 | public void zip() { 344 | Observable observable1 = Observable.interval(5, TimeUnit.SECONDS, mTestScheduler) 345 | .skip(1) 346 | .take(5) 347 | .doOnNext(System.out::println); 348 | Observable observable2 = Observable.interval(8, TimeUnit.SECONDS, mTestScheduler) 349 | .take(4) 350 | .map(aLong -> (char) ('A' + aLong)) 351 | .doOnNext(System.out::println); 352 | 353 | Observable.zip(observable1, observable2, (aLong, aChar) -> aLong + String.valueOf(aChar)) 354 | .subscribe(mList::add); 355 | 356 | mTestScheduler.advanceTimeBy(50, TimeUnit.SECONDS); 357 | assertEquals(mList, Arrays.asList("1A", "2B", "3C", "4D")); 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/ConditionalAndBooleanOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import rx.Observable; 13 | import rx.schedulers.TestScheduler; 14 | 15 | import static junit.framework.Assert.assertEquals; 16 | 17 | /** 18 | * Created by geniusmart on 16/11/2. 19 | *

20 | * Operators that originate new Observables. 21 | */ 22 | public class ConditionalAndBooleanOperatorsTest { 23 | 24 | private TestScheduler mTestScheduler; 25 | private List mList; 26 | 27 | @Before 28 | public void setUp() { 29 | mTestScheduler = new TestScheduler(); 30 | mList = new ArrayList<>(); 31 | } 32 | 33 | /** 34 | * determine whether all items emitted by an Observable meet some criteria 35 | * 36 | * @see RxMarbles diagrams all 37 | */ 38 | @Test 39 | public void all() { 40 | Observable.just(1, 2, 3, 4, 5) 41 | .doOnNext(System.out::println) 42 | .all(x -> x < 10) 43 | .subscribe(mList::add); 44 | assertEquals(mList, Collections.singletonList(true)); 45 | } 46 | 47 | /** 48 | * @see RxMarbles diagrams exists 49 | */ 50 | @Test 51 | public void exists() { 52 | Observable.just(2, 30, 22, 5, 60, 1) 53 | .doOnNext(System.out::println) 54 | .exists(integer -> integer > 10) 55 | .subscribe(mList::add); 56 | assertEquals(mList, Collections.singletonList(true)); 57 | } 58 | 59 | /** 60 | * determine whether an Observable emits a particular item or not 61 | * 62 | * @see RxMarbles diagrams contains 63 | */ 64 | @Test 65 | public void contains() { 66 | Observable.just(2, 30, 22, 5, 60, 1) 67 | .contains(22) 68 | .subscribe(mList::add); 69 | assertEquals(mList, Collections.singletonList(true)); 70 | } 71 | 72 | /** 73 | * determine whether two Observables emit the same sequence of items 74 | * 75 | * @see RxMarbles diagrams exists 76 | */ 77 | @Test 78 | public void sequenceEqual() { 79 | 80 | Observable o1 = Observable.just(1L, 2L, 3L, 4L, 5L) 81 | .delay(1000, TimeUnit.SECONDS, mTestScheduler); 82 | Observable o2 = Observable.interval(20, TimeUnit.SECONDS, mTestScheduler) 83 | .skip(1) 84 | .take(5); 85 | 86 | Observable.sequenceEqual(o1, o2) 87 | .subscribe(mList::add); 88 | 89 | mTestScheduler.advanceTimeBy(1200, TimeUnit.SECONDS); 90 | 91 | assertEquals(mList, Collections.singletonList(true)); 92 | } 93 | 94 | /** 95 | * given two or more source Observables, emit all of the items from only the first of these 96 | * Observables to emit an item 97 | * 98 | * @see RxMarbles diagrams amb 99 | */ 100 | @Test 101 | public void amb() { 102 | Observable o1 = Observable.just(20, 40, 60) 103 | .delay(500, TimeUnit.SECONDS, mTestScheduler); 104 | 105 | Observable o2 = Observable.just(1, 2, 3) 106 | .delay(200, TimeUnit.SECONDS, mTestScheduler); 107 | 108 | Observable o3 = Observable.just(0, 0, 0) 109 | .delay(1000, TimeUnit.SECONDS, mTestScheduler); 110 | 111 | Observable.amb(o1, o2, o3) 112 | .subscribe(mList::add); 113 | 114 | mTestScheduler.advanceTimeBy(1000, TimeUnit.SECONDS); 115 | assertEquals(mList, Arrays.asList(1, 2, 3)); 116 | } 117 | 118 | /** 119 | * emit items from the source Observable, or a default item if the source Observable 120 | * emits nothing 121 | * 122 | * @see 123 | * defaultIfEmpty 124 | */ 125 | @Test 126 | public void defaultIfEmpty() { 127 | Observable.empty() 128 | .defaultIfEmpty("geniusmart") 129 | .subscribe(mList::add); 130 | assertEquals(mList, Collections.singletonList("geniusmart")); 131 | } 132 | 133 | /** 134 | * discard items emitted by an Observable until a second Observable emits an item 135 | * 136 | * @see RxMarbles diagrams skipUntil 137 | */ 138 | @Test 139 | public void skipUntil() { 140 | 141 | Observable o1 = Observable.interval(100, TimeUnit.SECONDS, mTestScheduler) 142 | .map(num -> num + 1) 143 | .take(9); 144 | 145 | Observable o2 = Observable.just(0, 0) 146 | .delay(550, TimeUnit.SECONDS, mTestScheduler); 147 | 148 | o1.skipUntil(o2) 149 | .subscribe(mList::add); 150 | 151 | mTestScheduler.advanceTimeBy(2000, TimeUnit.SECONDS); 152 | assertEquals(mList, Arrays.asList(6L, 7L, 8L, 9L)); 153 | } 154 | 155 | /** 156 | * discard items emitted by an Observable until a specified condition becomes false 157 | * 158 | * @see 159 | * skipWhile 160 | */ 161 | @Test 162 | public void skipWhile() { 163 | Observable.interval(100, TimeUnit.SECONDS, mTestScheduler) 164 | .map(num -> num + 1) 165 | .take(7) 166 | .skipWhile(aLong -> aLong != 4) 167 | .subscribe(mList::add); 168 | 169 | mTestScheduler.advanceTimeBy(1000, TimeUnit.SECONDS); 170 | assertEquals(mList, Arrays.asList(4L, 5L, 6L, 7L)); 171 | } 172 | 173 | /** 174 | * discard items emitted by an Observable after a second Observable emits an item or 175 | * terminates 176 | */ 177 | @Test 178 | public void takeUntil() { 179 | Observable.just(1, 2, 3, 4) 180 | .takeUntil(integer -> integer > 2) 181 | .subscribe(mList::add); 182 | assertEquals(mList, Arrays.asList(1, 2, 3)); 183 | 184 | mList.clear(); 185 | Observable.just(1, 2, 3, 4) 186 | .takeUntil(integer -> integer < 10) 187 | .subscribe(mList::add); 188 | assertEquals(mList, Collections.singletonList(1)); 189 | } 190 | 191 | /** 192 | * TakeUntil — discard items emitted by an Observable after a second Observable emits an 193 | * item or terminates 194 | * 195 | * @see RxMarbles diagrams takeUntil 196 | */ 197 | @Test 198 | public void takeUntilWithObservable() { 199 | 200 | Observable observable = Observable.just(0, 0) 201 | .delay(550, TimeUnit.MILLISECONDS, mTestScheduler); 202 | 203 | Observable.interval(0, 100, TimeUnit.MILLISECONDS, mTestScheduler) 204 | .skip(1) 205 | .takeUntil(observable) 206 | .subscribe(mList::add); 207 | 208 | mTestScheduler.advanceTimeBy(10, TimeUnit.SECONDS); 209 | assertEquals(mList, Arrays.asList(1L, 2L, 3L, 4L, 5L)); 210 | } 211 | 212 | /** 213 | * discard items emitted by an Observable after a specified condition becomes false 214 | * 215 | * @see 216 | * takeWhile 217 | */ 218 | @Test 219 | public void takeWhile() { 220 | Observable.interval(100, TimeUnit.SECONDS, mTestScheduler) 221 | .map(num -> num + 1) 222 | .take(7) 223 | .takeWhile(aLong -> aLong != 4) 224 | .subscribe(mList::add); 225 | 226 | mTestScheduler.advanceTimeBy(1000, TimeUnit.SECONDS); 227 | assertEquals(mList, Arrays.asList(1L, 2L, 3L)); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/ConnectableOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | import com.geniusmart.rxjava.utils.OperatorUtils; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.List; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | import rx.Observable; 15 | import rx.Subscriber; 16 | import rx.Subscription; 17 | import rx.observables.ConnectableObservable; 18 | import rx.schedulers.Schedulers; 19 | import rx.schedulers.TestScheduler; 20 | 21 | import static junit.framework.Assert.assertEquals; 22 | import static junit.framework.Assert.assertTrue; 23 | 24 | /** 25 | * Created by geniusmart on 16/11/6. 26 | *

27 | * Specialty Observables that have more precisely-controlled subscription dynamics 28 | */ 29 | public class ConnectableOperatorsTest { 30 | 31 | private TestScheduler mTestScheduler; 32 | private List mList; 33 | 34 | @Before 35 | public void setUp() { 36 | mTestScheduler = new TestScheduler(); 37 | mList = new ArrayList<>(); 38 | } 39 | 40 | /** 41 | * convert an ordinary Observable into a connectable Observable 42 | * 43 | * @see publish 44 | */ 45 | @Test 46 | public void publish() { 47 | //创建ConnectableObservable 48 | ConnectableObservable publish = 49 | Observable.just(1, 2, 3) 50 | .publish(); 51 | 52 | //此时并不会马上订阅数据 53 | publish.subscribe(mList::add); 54 | assertTrue(mList.isEmpty()); 55 | 56 | //开始订阅数据 57 | publish.connect(); 58 | assertEquals(mList, Arrays.asList(1, 2, 3)); 59 | } 60 | 61 | /** 62 | * instruct a connectable Observable to begin emitting items to its subscribers 63 | *

64 | * 此例子根据connect的官方 marble diagrams 进行实现 65 | * 66 | * @see connect.png 67 | * @see connect 68 | */ 69 | @Test 70 | public void connect() { 71 | 72 | List list1 = new ArrayList<>(); 73 | List list2 = new ArrayList<>(); 74 | List list3 = new ArrayList<>(); 75 | 76 | //构造1,2,3的数据流,每隔3s发射数据 77 | ConnectableObservable connectableObservable = Observable.create(new Observable.OnSubscribe() { 78 | @Override 79 | public void call(Subscriber subscriber) { 80 | subscriber.onNext(1); 81 | OperatorUtils.sleep(3000); 82 | subscriber.onNext(2); 83 | OperatorUtils.sleep(3000); 84 | subscriber.onNext(3); 85 | } 86 | }).publish(); 87 | 88 | System.out.println("Subscriber1-0s后开始订阅数据"); 89 | //立刻订阅完整的数据流 90 | connectableObservable.doOnNext(num -> System.out.println("Subscriber1-->" + num)) 91 | .subscribe(list1::add); 92 | 93 | //延迟6s后再订阅,将只订阅到3的数据流 94 | connectableObservable.delaySubscription(6, TimeUnit.SECONDS, Schedulers.newThread()) 95 | .doOnSubscribe(()->{ 96 | System.out.println("Subscriber2-6s后开始订阅数据"); 97 | }) 98 | .doOnNext(num -> System.out.println("Subscriber2-->" + num)) 99 | .subscribe(list2::add); 100 | 101 | //延迟1s后再订阅,将订阅到1,2的数据流 102 | connectableObservable.delaySubscription(1, TimeUnit.SECONDS, Schedulers.newThread()) 103 | .doOnSubscribe(()->{ 104 | System.out.println("Subscriber3-1s后开始订阅数据"); 105 | }) 106 | .doOnNext(num -> System.out.println("Subscriber3-->" + num)) 107 | .subscribe(list3::add); 108 | 109 | 110 | //延时2s执行connect() 111 | OperatorUtils.sleep(2000); 112 | System.out.println("Observable 2s后触发connect()"); 113 | connectableObservable.connect(); 114 | 115 | assertEquals(list1, Arrays.asList(1, 2, 3)); 116 | assertEquals(list2, Collections.singletonList(3)); 117 | assertEquals(list3, Arrays.asList(1, 2, 3)); 118 | } 119 | 120 | /** 121 | * make a Connectable Observable behave like an ordinary Observable 122 | *

123 | * 此用例介绍refCount的概念,官方 marble diagrams 的实现详见refCount2()用例 124 | * 125 | * @see ReactiveX 126 | * documentation: RefCount 127 | */ 128 | @Test 129 | public void refCount() { 130 | //构造1,2,3的数据流,每隔3s发射数据 131 | ConnectableObservable connectableObservable = Observable.create(new Observable.OnSubscribe() { 132 | @Override 133 | public void call(Subscriber subscriber) { 134 | subscriber.onNext(1); 135 | subscriber.onNext(2); 136 | subscriber.onNext(3); 137 | subscriber.onCompleted(); 138 | } 139 | }).publish(); 140 | 141 | Observable observable = connectableObservable.refCount(); 142 | 143 | observable.subscribe(mList::add); 144 | 145 | assertEquals(mList, Arrays.asList(1, 2, 3)); 146 | 147 | } 148 | 149 | /** 150 | * make a Connectable Observable behave like an ordinary Observable 151 | *

152 | * 此例子根据connect的官方 marble diagrams 进行实现,refCount将ConnectableObservable转换为普通的Observable, 153 | * 但仍然保持了hot数据流的特点,可对比下文的without_public_and_refCount()中cold数据流的区别 154 | * 155 | * @see RefCount.png 156 | * @see ReactiveX 157 | * documentation: RefCount 158 | */ 159 | @Test 160 | public void refCount2() { 161 | 162 | List list1 = new ArrayList<>(); 163 | List list2 = new ArrayList<>(); 164 | 165 | //构造0,1,2的数据流,每隔300ms发射数据 166 | Observable observable = Observable.interval(0, 300, TimeUnit.MILLISECONDS) 167 | .take(3) 168 | .publish() 169 | .refCount() 170 | .doOnNext(System.out::println) 171 | .doOnCompleted(() -> System.out.println("onCompleted")) 172 | .subscribeOn(Schedulers.newThread()); 173 | 174 | Subscription subscribe1 = observable.subscribe(list1::add); 175 | OperatorUtils.sleep(100); 176 | Subscription subscribe2 = observable.subscribe(list2::add); 177 | 178 | OperatorUtils.sleep(400); 179 | subscribe1.unsubscribe(); 180 | subscribe2.unsubscribe(); 181 | 182 | OperatorUtils.sleep(600); 183 | 184 | assertEquals(list1, Arrays.asList(0L, 1L)); 185 | assertEquals(list2, Collections.singletonList(1L)); 186 | 187 | } 188 | 189 | /** 190 | * 此方法与refCount2比对,展示hot与cold数据流的区别 191 | */ 192 | @Test 193 | public void without_publish_and_refCount() { 194 | 195 | List list1 = new ArrayList<>(); 196 | List list2 = new ArrayList<>(); 197 | 198 | //构造1,2,3的数据流,每隔3s发射数据 199 | Observable observable = Observable.interval(0, 300, TimeUnit.MILLISECONDS) 200 | .take(3) 201 | .doOnNext(System.out::println) 202 | .doOnCompleted(() -> System.out.println("onCompleted")) 203 | .subscribeOn(Schedulers.newThread()); 204 | 205 | Subscription subscribe1 = observable.subscribe(list1::add); 206 | OperatorUtils.sleep(100); 207 | Subscription subscribe2 = observable.subscribe(list2::add); 208 | 209 | OperatorUtils.sleep(400); 210 | subscribe1.unsubscribe(); 211 | subscribe2.unsubscribe(); 212 | 213 | OperatorUtils.sleep(600); 214 | 215 | assertEquals(list1, Arrays.asList(0L, 1L)); 216 | assertEquals(list2, Arrays.asList(0L, 1L)); 217 | 218 | } 219 | 220 | /** 221 | * ensure that all observers see the same sequence of emitted items, even if they subscribe 222 | * after the Observable has begun emitting items 223 | *

224 | * 此例子根据replay的官方 marble diagrams 进行实现 225 | * 226 | * @see replay.png 227 | * @see ReactiveX operators 228 | * documentation: Replay 229 | */ 230 | @Test 231 | public void replay() { 232 | List list1 = new ArrayList<>(); 233 | List list2 = new ArrayList<>(); 234 | 235 | //构造1,2,3的数据流,每隔3s发射数据 236 | ConnectableObservable connectableObservable = Observable.create(new Observable.OnSubscribe() { 237 | @Override 238 | public void call(Subscriber subscriber) { 239 | System.out.println("Observable只执行一次"); 240 | subscriber.onNext(1); 241 | OperatorUtils.sleep(3000); 242 | subscriber.onNext(2); 243 | OperatorUtils.sleep(3000); 244 | subscriber.onNext(3); 245 | subscriber.onCompleted(); 246 | } 247 | }).replay(); 248 | 249 | System.out.println("Subscriber1-0s后开始订阅数据"); 250 | //立刻订阅完整的数据流 251 | connectableObservable.doOnNext(num -> System.out.println("Subscriber1-->" + num)) 252 | .subscribe(list1::add); 253 | 254 | connectableObservable 255 | .delaySubscription(6, TimeUnit.SECONDS, Schedulers.newThread()) 256 | .doOnSubscribe(()->{ 257 | System.out.println("Subscriber2-6s后开始订阅数据"); 258 | }) 259 | .doOnNext(num -> System.out.println("Subscriber2-->" + num)) 260 | .subscribe(list2::add); 261 | 262 | //延时2s执行connect() 263 | OperatorUtils.sleep(2000); 264 | System.out.println("Observable 2s后触发connect()"); 265 | connectableObservable.connect(); 266 | 267 | assertEquals(list1, Arrays.asList(1, 2, 3)); 268 | assertEquals(list2, Arrays.asList(1, 2, 3)); 269 | } 270 | 271 | /** 272 | * remember the sequence of items emitted by the Observable and emit the same sequence to 273 | * future Subscribers 274 | *

275 | * cache操作函数和replay类似,但无需使用ConnectableObservable 276 | *

277 | * 此例子实现了该 marble diagrams :https://raw.githubusercontent.com/wiki/ReactiveX/RxJava/images/rx-operators/cache.png 278 | */ 279 | @Test 280 | public void cache() { 281 | 282 | List list1 = new ArrayList<>(); 283 | List list2 = new ArrayList<>(); 284 | 285 | //构造1,2,3的数据流,每隔3s发射数据 286 | Observable cacheObservable = Observable.create(new Observable.OnSubscribe() { 287 | @Override 288 | public void call(Subscriber subscriber) { 289 | System.out.println("Observable只执行一次"); 290 | subscriber.onNext(1); 291 | subscriber.onNext(2); 292 | subscriber.onNext(3); 293 | OperatorUtils.sleep(1000); 294 | subscriber.onNext(4); 295 | subscriber.onCompleted(); 296 | } 297 | }).cache().subscribeOn(Schedulers.newThread()); 298 | 299 | cacheObservable.subscribe(list1::add); 300 | OperatorUtils.sleep(800); 301 | cacheObservable.subscribe(list2::add); 302 | 303 | //确保所有线程能执行完毕 304 | OperatorUtils.sleep(2000); 305 | assertEquals(list1, Arrays.asList(1, 2, 3, 4)); 306 | assertEquals(list2, Arrays.asList(1, 2, 3, 4)); 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/ConvertObservablesTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.io.Serializable; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.HashMap; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.concurrent.ExecutionException; 15 | import java.util.concurrent.Future; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | import rx.Observable; 19 | import rx.functions.Func1; 20 | import rx.observables.BlockingObservable; 21 | import rx.schedulers.TestScheduler; 22 | 23 | import static junit.framework.Assert.assertEquals; 24 | 25 | /** 26 | * Created by geniusmart on 16/11/6. 27 | *

28 | * convert an Observable into another object or data structure 29 | * 30 | * @see ReactiveX documentation: 31 | * To 32 | */ 33 | public class ConvertObservablesTest { 34 | 35 | private TestScheduler mTestScheduler; 36 | private List mList; 37 | 38 | @Before 39 | public void setUp() { 40 | mTestScheduler = new TestScheduler(); 41 | mList = new ArrayList<>(); 42 | } 43 | 44 | /** 45 | * Returns an {@link Iterator} that iterates over all items emitted by BlockingObservable. 46 | * 47 | * @see 48 | * getIterator 49 | */ 50 | @Test 51 | public void getIterator() { 52 | 53 | Observable observable = Observable.interval(1, TimeUnit.SECONDS) 54 | .take(3); 55 | 56 | Iterator iterator = BlockingObservable.from(observable) 57 | .getIterator(); 58 | 59 | while (iterator.hasNext()) { 60 | mList.add(iterator.next()); 61 | } 62 | assertEquals(mList, Arrays.asList(0L, 1L, 2L)); 63 | 64 | } 65 | 66 | /** 67 | * Converts an Observable into a BlockingObservable(an Observable with blocking operators). 68 | */ 69 | @Test 70 | public void toBlocking() { 71 | 72 | Observable.interval(1, TimeUnit.SECONDS) 73 | .take(3) 74 | .toBlocking() 75 | .forEach(mList::add); 76 | 77 | assertEquals(mList, Arrays.asList(0L, 1L, 2L)); 78 | 79 | } 80 | 81 | /** 82 | * Returns a {@link Future} representing the single value emitted by this BlockingObservable. 83 | * 84 | * @see 85 | * toFuture 86 | */ 87 | @Test 88 | public void toFuture() throws ExecutionException, InterruptedException { 89 | 90 | //只能发射一个数据 91 | Future future = Observable.timer(1, TimeUnit.SECONDS) 92 | .toBlocking() 93 | .toFuture(); 94 | 95 | long aLong = future.get(); 96 | assertEquals(aLong, 0L); 97 | } 98 | 99 | /** 100 | * Converts this BlockingObservable into an {@link Iterable}. 101 | * 102 | * @see 103 | * toIterable 104 | */ 105 | @Test 106 | public void toIterable() { 107 | 108 | Iterable iterable = Observable.just(1, 2, 3) 109 | .toBlocking() 110 | .toIterable(); 111 | 112 | Iterator iterator = iterable.iterator(); 113 | 114 | while (iterator.hasNext()) { 115 | mList.add(iterator.next()); 116 | } 117 | assertEquals(mList, Arrays.asList(1, 2, 3)); 118 | 119 | } 120 | 121 | /** 122 | * Returns an Observable that emits a single item, a list composed of all the 123 | * items emitted by the source Observable. 124 | * 125 | * @see toList 126 | */ 127 | @Test 128 | public void toList() { 129 | 130 | Observable.interval(1, TimeUnit.SECONDS, mTestScheduler) 131 | .take(6) 132 | .toList() 133 | .doOnNext(num -> { 134 | System.out.println("只触发一次,value=" + num); 135 | }) 136 | .subscribe(mList::addAll); 137 | 138 | mTestScheduler.advanceTimeBy(100, TimeUnit.SECONDS); 139 | 140 | assertEquals(mList, Arrays.asList(0L, 1L, 2L, 3L, 4L, 5L)); 141 | } 142 | 143 | /** 144 | * Returns an Observable that emits a single HashMap containing all items emitted by the source 145 | * Observable, 146 | * mapped by the keys returned by a specified function 147 | * 148 | * @see toMap 149 | */ 150 | @Test 151 | public void toMap() { 152 | 153 | Map map = new HashMap<>(); 154 | 155 | Observable.just("one", "two", "three") 156 | .toMap(s -> "key" + s) 157 | .subscribe(map::putAll); 158 | 159 | System.out.println(map); 160 | } 161 | 162 | /** 163 | * Returns an Observable that emits a single HashMap containing all items emitted by the source 164 | * Observable, 165 | * mapped by the keys returned by a specified function 166 | * 167 | * @see toMap 168 | */ 169 | @Test 170 | public void toMultiMap() { 171 | 172 | Observable.just(Arrays.asList(1, 2), Arrays.asList("A", "B")) 173 | .toMultimap(new Func1, Object>() { 174 | @Override 175 | public Object call(List list) { 176 | return list.get(0); 177 | } 178 | }) 179 | .subscribe(System.out::println); 180 | } 181 | 182 | /** 183 | * @see toSortedList 184 | */ 185 | @Test 186 | public void toSortedList() { 187 | 188 | Observable.just(2, 5, 1, 6, 3, 4) 189 | .toSortedList() 190 | .subscribe(mList::addAll); 191 | assertEquals(mList, Arrays.asList(1, 2, 3, 4, 5, 6)); 192 | 193 | //以下代码进行倒序 194 | mList.clear(); 195 | Observable.just(2, 5, 1, 6, 3, 4) 196 | .toSortedList((integer1, integer2) -> integer2 - integer1) 197 | .subscribe(mList::addAll); 198 | assertEquals(mList, Arrays.asList(6, 5, 4, 3, 2, 1)); 199 | } 200 | 201 | /** 202 | * Converts the source Observable into an Observable> that emits the 203 | * source Observable as its single emission. 204 | * 205 | * @see ReactiveX operators 206 | * documentation: To 207 | */ 208 | @Test 209 | public void nest() { 210 | 211 | Observable observable = Observable.just(1, 2, 3); 212 | 213 | //将observable本身作为数据流发射出去 214 | observable.nest() 215 | .subscribe(mList::add); 216 | 217 | assertEquals(mList, Collections.singletonList(observable)); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/CreatingOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import rx.Observable; 13 | import rx.Subscriber; 14 | import rx.schedulers.TestScheduler; 15 | import rx.util.async.Async; 16 | 17 | import static junit.framework.Assert.assertEquals; 18 | import static junit.framework.Assert.assertNotSame; 19 | import static junit.framework.Assert.assertTrue; 20 | 21 | /** 22 | * Created by geniusmart on 2016/11/4. 23 | *

24 | * Operators that originate new Observables. 25 | */ 26 | public class CreatingOperatorsTest { 27 | 28 | private TestScheduler mTestScheduler; 29 | private List mList; 30 | 31 | @Before 32 | public void setUp() { 33 | mTestScheduler = new TestScheduler(); 34 | mList = new ArrayList<>(); 35 | } 36 | 37 | /** 38 | * create an Observable from scratch by calling observer methods programmatically 39 | * 40 | * @see 41 | * ReactiveX operators documentation: Create 42 | */ 43 | @Test 44 | public void create() { 45 | Observable.create(new Observable.OnSubscribe() { 46 | @Override 47 | public void call(Subscriber subscriber) { 48 | subscriber.onNext(1); 49 | subscriber.onNext(3); 50 | subscriber.onNext(5); 51 | subscriber.onError(new ClassCastException()); 52 | subscriber.onNext(7); 53 | subscriber.onCompleted(); 54 | } 55 | }).subscribe(new Subscriber() { 56 | @Override 57 | public void onCompleted() { 58 | System.out.println("onCompleted"); 59 | } 60 | 61 | @Override 62 | public void onError(Throwable e) { 63 | System.out.println(e.getClass()); 64 | } 65 | 66 | @Override 67 | public void onNext(Integer integer) { 68 | mList.add(integer); 69 | } 70 | }); 71 | 72 | assertEquals(mList, Arrays.asList(1, 3, 5)); 73 | 74 | } 75 | 76 | /** 77 | * do not create the Observable until the observer subscribes, and create a fresh Observable 78 | * for each observer 79 | *

80 | * 此例子用于说明 defer 产生的数据流都是全新的 81 | * 82 | * @see ReactiveX operators 83 | * documentation: Defer 84 | */ 85 | @Test 86 | public void defer1() { 87 | 88 | List> list = new ArrayList<>(); 89 | 90 | Observable deferObservable = Observable.defer(() -> { 91 | Observable observable = Observable.just(1, 2, 3); 92 | list.add(observable); 93 | return observable; 94 | }); 95 | 96 | // 两次订阅,每次都将产生全新的Observable 97 | deferObservable.subscribe(); 98 | deferObservable.subscribe(); 99 | 100 | assertNotSame(list.get(0), list.get(1)); 101 | } 102 | 103 | /** 104 | * do not create the Observable until the observer subscribes, and create a fresh Observable 105 | * for each observer 106 | *

107 | * 此例子用于说明defer延迟订阅的作用 108 | * 109 | * @see ReactiveX operators 110 | * documentation: Defer 111 | * @see 【译】使用RxJava实现延迟订阅 112 | */ 113 | @Test 114 | public void defer2() { 115 | 116 | class Person { 117 | public String name = "nobody"; 118 | 119 | public Observable getJustObservable() { 120 | //创建的时候便获取name值 121 | return Observable.just(name); 122 | } 123 | 124 | public Observable getDeferObservable() { 125 | //订阅的时候才获取name值 126 | return Observable.defer(this::getJustObservable); 127 | } 128 | } 129 | 130 | Person person = new Person(); 131 | Observable justObservable = person.getJustObservable(); 132 | Observable deferObservable = person.getDeferObservable(); 133 | 134 | // 数据改变之前 135 | justObservable.subscribe(mList::add); 136 | assertEquals(mList, Collections.singletonList("nobody")); 137 | 138 | mList.clear(); 139 | deferObservable.subscribe(mList::add); 140 | assertEquals(mList, Collections.singletonList("nobody")); 141 | 142 | person.name = "geniusmart"; 143 | 144 | // 数据改变之后 145 | mList.clear(); 146 | justObservable.subscribe(mList::add); 147 | assertEquals(mList, Collections.singletonList("nobody")); 148 | 149 | mList.clear(); 150 | deferObservable.subscribe(mList::add); 151 | assertEquals(mList, Collections.singletonList("geniusmart")); 152 | 153 | } 154 | 155 | /** 156 | * create an Observable that emits no items but terminates normally 157 | * 158 | * @see ReactiveX 159 | * operators documentation: Empty 160 | */ 161 | @Test 162 | public void empty() { 163 | Observable.empty() 164 | .doOnNext(value -> mList.add(value)) 165 | .doOnCompleted(() -> mList.add("completed")) 166 | .subscribe(mList::add); 167 | 168 | assertEquals(mList, Collections.singletonList("completed")); 169 | } 170 | 171 | /** 172 | * create an Observable that emits no items and does not terminate 173 | * 174 | * @see ReactiveX 175 | * operators documentation: Empty 176 | */ 177 | @Test 178 | public void never() { 179 | Observable.never() 180 | .doOnNext(value -> mList.add(value)) 181 | .doOnCompleted(() -> mList.add("completed")) 182 | .subscribe(mList::add); 183 | assertTrue(mList.isEmpty()); 184 | } 185 | 186 | /** 187 | * create an Observable that emits no items and terminates with an error 188 | * 189 | * @see ReactiveX 190 | * operators documentation: Empty 191 | */ 192 | @Test 193 | public void error() { 194 | Observable.error(new NullPointerException()) 195 | .subscribe( 196 | value -> mList.add(value), 197 | throwable -> mList.add("error"), 198 | () -> mList.add("completed") 199 | ); 200 | 201 | assertEquals(mList, Collections.singletonList("error")); 202 | } 203 | 204 | /** 205 | * convert some other object or data structure into an Observable 206 | * 207 | * @see ReactiveX operators 208 | * documentation: From 209 | */ 210 | @Test 211 | public void from() { 212 | String[] values = new String[]{"a", "b", "c"}; 213 | Observable.from(values) 214 | .subscribe(mList::add); 215 | assertEquals(mList, Arrays.asList("a", "b", "c")); 216 | } 217 | 218 | /** 219 | * create an Observable that emits a sequence of integers spaced by a particular time interval 220 | * 221 | * @see ReactiveX operators 222 | * documentation: Interval 223 | */ 224 | @Test 225 | public void interval() { 226 | Observable.interval(100, TimeUnit.MILLISECONDS, mTestScheduler) 227 | .subscribe(mList::add); 228 | 229 | //时间提早400ms前 230 | mTestScheduler.advanceTimeBy(400, TimeUnit.MILLISECONDS); 231 | assertEquals(mList, Arrays.asList(0L, 1L, 2L, 3L)); 232 | 233 | //时间提早(400 + 200)ms前 234 | mTestScheduler.advanceTimeBy(200, TimeUnit.MILLISECONDS); 235 | assertEquals(mList, Arrays.asList(0L, 1L, 2L, 3L, 4L, 5L)); 236 | } 237 | 238 | /** 239 | * convert an object or a set of objects into an Observable that emits that or those objects 240 | * 241 | * @see ReactiveX operators 242 | * documentation: Just 243 | */ 244 | @Test 245 | public void just() { 246 | Observable.just(1, 2, 3, 4, 5) 247 | .subscribe(mList::add); 248 | assertEquals(mList, Arrays.asList(1, 2, 3, 4, 5)); 249 | } 250 | 251 | /** 252 | * create an Observable that emits a range of sequential integers 253 | * 254 | * @see ReactiveX operators 255 | * documentation: Range 256 | */ 257 | @Test 258 | public void range() { 259 | Observable.range(2, 6) 260 | .subscribe(mList::add); 261 | assertEquals(mList, Arrays.asList(2, 3, 4, 5, 6, 7)); 262 | } 263 | 264 | /** 265 | * create an Observable that emits a particular item or sequence of items repeatedly 266 | * 267 | * @see ReactiveX operators 268 | * documentation: Repeat 269 | */ 270 | @Test 271 | public void repeat() { 272 | Observable.just(1, 2) 273 | .repeat(3) 274 | .subscribe(mList::add); 275 | assertEquals(mList, Arrays.asList(1, 2, 1, 2, 1, 2)); 276 | } 277 | 278 | /** 279 | * 实现一个延迟数秒的重订阅 280 | * 281 | * @see ReactiveX operators 282 | * documentation: Repeat 283 | * @see repeatWhen.png 284 | * @see repeatWhen()和retryWhen() 285 | */ 286 | @Test 287 | public void repeatWhen() { 288 | 289 | Observable.just(1, 2) 290 | .repeatWhen(completed -> completed.delay(5, TimeUnit.SECONDS, mTestScheduler)) 291 | .subscribe(mList::add); 292 | 293 | mTestScheduler.advanceTimeBy(3, TimeUnit.SECONDS); 294 | assertEquals(mList, Arrays.asList(1, 2)); 295 | 296 | mTestScheduler.advanceTimeBy(2, TimeUnit.SECONDS); 297 | assertEquals(mList, Arrays.asList(1, 2, 1, 2)); 298 | 299 | } 300 | 301 | /** 302 | * create an Observable that emits the return value of a function 303 | * 304 | * @see 305 | * RxJava Wiki: start() 306 | */ 307 | @Test 308 | public void start() { 309 | //只执行一次 310 | Observable startObservable = Async.start(() -> 1); 311 | startObservable.subscribe(mList::add); 312 | assertEquals(mList, Collections.singletonList(1)); 313 | 314 | mList.clear(); 315 | startObservable.subscribe(System.out::println); 316 | assertTrue(mList.isEmpty()); 317 | } 318 | 319 | /** 320 | * create an Observable that emits a single item after a given delay 321 | * 322 | * @see ReactiveX operators 323 | * documentation: Timer 324 | */ 325 | @Test 326 | public void timer() { 327 | 328 | //仅发射0的数值,延迟100s 329 | Observable.timer(100, TimeUnit.SECONDS, mTestScheduler) 330 | .subscribe(mList::add); 331 | 332 | assertTrue(mList.isEmpty()); 333 | 334 | mTestScheduler.advanceTimeBy(100, TimeUnit.SECONDS); 335 | assertEquals(mList, Collections.singletonList(0L)); 336 | } 337 | 338 | } 339 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/CustomOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | /** 4 | * Created by geniusmart on 2016/11/9. 5 | *

6 | * TODO:自定义操作符 7 | */ 8 | public class CustomOperatorsTest { 9 | } 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/ErrorHandlingOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import rx.Observable; 12 | import rx.Subscriber; 13 | import rx.functions.Func1; 14 | import rx.functions.Func2; 15 | import rx.schedulers.TestScheduler; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertTrue; 19 | 20 | /** 21 | * Created by geniusmart on 16/11/6. 22 | *

23 | * Operators that help to recover from error notifications from an Observable 24 | */ 25 | public class ErrorHandlingOperatorsTest { 26 | 27 | private TestScheduler mTestScheduler; 28 | private List mList; 29 | 30 | @Before 31 | public void setUp() { 32 | mTestScheduler = new TestScheduler(); 33 | mList = new ArrayList<>(); 34 | } 35 | 36 | /** 37 | * instructs an Observable to emit a particular item when it encounters an error, and then 38 | * terminate normally 39 | *

40 | * 根据官方 marble diagrams 实现 41 | * 42 | * @see ReactiveX operators 43 | * documentation: Catch 44 | */ 45 | @Test 46 | public void onErrorReturn() { 47 | Observable.create(new Observable.OnSubscribe() { 48 | @Override 49 | public void call(Subscriber subscriber) { 50 | subscriber.onNext("○"); 51 | subscriber.onNext("○"); 52 | subscriber.onNext("○"); 53 | subscriber.onError(new ArithmeticException()); 54 | } 55 | }) 56 | .onErrorReturn(throwable -> "◇") 57 | .subscribe(mList::add); 58 | assertEquals(mList, Arrays.asList("○", "○", "○", "◇")); 59 | } 60 | 61 | /** 62 | * instructs an Observable to begin emitting a second Observable sequence if it encounters an 63 | * error 64 | *

65 | * 根据官方 marble diagrams 实现 66 | * 67 | * @see ReactiveX operators 68 | * documentation: Catch 69 | */ 70 | @Test 71 | public void onErrorResumeNext() { 72 | Observable.create(new Observable.OnSubscribe() { 73 | @Override 74 | public void call(Subscriber subscriber) { 75 | subscriber.onNext(1); 76 | subscriber.onNext(2); 77 | subscriber.onNext(3); 78 | subscriber.onError(new NullPointerException()); 79 | } 80 | }) 81 | .onErrorResumeNext(Observable.just(4, 5)) 82 | .subscribe(mList::add); 83 | assertEquals(mList, Arrays.asList(1, 2, 3, 4, 5)); 84 | } 85 | 86 | /** 87 | * instructs an Observable to begin emitting a second Observable sequence if it encounters an 88 | * error 89 | */ 90 | @Test 91 | public void onErrorResumeNext2() { 92 | Observable.create(new Observable.OnSubscribe() { 93 | @Override 94 | public void call(Subscriber subscriber) { 95 | subscriber.onNext(1); 96 | subscriber.onNext(2); 97 | subscriber.onError(new NullPointerException()); 98 | } 99 | }).onErrorResumeNext(throwable -> { 100 | if (throwable instanceof NullPointerException) { 101 | return Observable.just(3, 4); 102 | } 103 | return Observable.just(5, 6); 104 | }).subscribe(mList::add); 105 | 106 | assertEquals(mList, Arrays.asList(1, 2, 3, 4)); 107 | } 108 | 109 | /** 110 | * instructs an Observable to continue emitting items after it encounters an exception (but not 111 | * another variety of throwable) 112 | * 113 | * @see ReactiveX operators 114 | * documentation: Catch 115 | */ 116 | @Test 117 | public void onExceptionResumeNext() { 118 | Observable.create(new Observable.OnSubscribe() { 119 | @Override 120 | public void call(Subscriber subscriber) { 121 | subscriber.onNext(1); 122 | subscriber.onNext(2); 123 | subscriber.onError(new Throwable("throwable")); 124 | } 125 | }) 126 | .onExceptionResumeNext(Observable.just(4)) 127 | .subscribe( 128 | mList::add, 129 | throwable -> System.out.println(throwable.getMessage()) 130 | ); 131 | 132 | //onExceptionResumeNext只处理Exception类型的error,其他类型(如Error和Throwable)的异常不进行处理 133 | assertEquals(mList, Arrays.asList(1, 2)); 134 | } 135 | 136 | /** 137 | * recover from an onError notification by continuing the sequence without error 138 | * 139 | *

140 | * 此例子根据replay的官方 marble diagrams 进行实现 141 | * @see ReactiveX operators 142 | * documentation: Retry 143 | */ 144 | @Test 145 | public void retry() { 146 | 147 | final Integer[] arrays = {0}; 148 | 149 | Observable.create(new Observable.OnSubscribe() { 150 | @Override 151 | public void call(Subscriber subscriber) { 152 | subscriber.onNext(1); 153 | subscriber.onNext(2); 154 | subscriber.onNext(3 / arrays[0]++); 155 | subscriber.onCompleted(); 156 | } 157 | }) 158 | .retry() 159 | .subscribe(mList::add); 160 | 161 | assertEquals(mList, Arrays.asList(1, 2, 1, 2, 3)); 162 | } 163 | 164 | /** 165 | * retryWhen破坏数据流的示例 166 | * 167 | * @see ReactiveX operators 168 | * documentation: Retry 169 | * @see retryWhen和repeatWhen 170 | */ 171 | @Test 172 | public void retryWhen_break_sequence() { 173 | 174 | // 错误的做法:破坏数据流,打断链式结构 175 | Observable.just(1, 2, 3) 176 | .retryWhen(throwableObservable -> Observable.just(1, 1, 1)) 177 | .subscribe(mList::add); 178 | //数据流被打断,订阅不到数据 179 | assertTrue(mList.isEmpty()); 180 | 181 | // 正确的做法:至少将throwableObservable作为返回结果,此时的retryWhen()等价于retry() 182 | Observable.just(1, 2, 3) 183 | .retryWhen(throwableObservable -> throwableObservable). 184 | subscribe(mList::add); 185 | //此处的数据流不会触发error,因此正常输出1,2,3的数列 186 | assertEquals(mList, Arrays.asList(1, 2, 3)); 187 | } 188 | 189 | /** 190 | * 使用retryWhen() + flatMap() + timer() 实现延迟重新订阅 191 | * 192 | * @see ReactiveX operators 193 | * documentation: Retry 194 | * @see retryWhen和repeatWhen 195 | */ 196 | @Test 197 | public void retryWhen_flatMap_timer() { 198 | 199 | Observable.create(subscriber -> { 200 | System.out.println("subscribing"); 201 | subscriber.onNext(1); 202 | subscriber.onNext(2); 203 | subscriber.onError(new RuntimeException("RuntimeException")); 204 | }) 205 | .retryWhen(observable -> 206 | observable.flatMap( 207 | (Func1>) throwable -> 208 | //延迟5s重新订阅 209 | Observable.timer(5, TimeUnit.SECONDS, mTestScheduler) 210 | ) 211 | ) 212 | .subscribe(num -> { 213 | System.out.println(num); 214 | mList.add(num); 215 | }); 216 | 217 | //时间提前10s,将发生1次订阅+2次重新订阅 218 | mTestScheduler.advanceTimeBy(10, TimeUnit.SECONDS); 219 | 220 | assertEquals(mList, Arrays.asList(1, 2, 1, 2, 1, 2)); 221 | } 222 | 223 | /** 224 | * 延迟策略与次数限制的重试机制结合起来 225 | * 226 | * @see ReactiveX operators 227 | * documentation: Retry 228 | * @see retryWhen和repeatWhen 229 | */ 230 | @Test 231 | public void retryWhen_zip_range_timer() { 232 | 233 | Observable.create((Subscriber subscriber) -> { 234 | System.out.println("subscribing"); 235 | subscriber.onNext(1); 236 | subscriber.onNext(2); 237 | subscriber.onError(new RuntimeException("always fails")); 238 | }) 239 | .retryWhen(observable -> 240 | observable.zipWith( 241 | Observable.range(1, 3), 242 | (Func2) (throwable, num) -> num 243 | ) 244 | .flatMap((Func1>) num -> { 245 | System.out.println("delay retry by " + num + " second(s)"); 246 | return Observable.timer(num, TimeUnit.SECONDS); 247 | })) 248 | .doOnNext(System.out::println) 249 | .doOnCompleted(() -> System.out.println("completed")) 250 | .toBlocking() 251 | .forEach(mList::add); 252 | 253 | //订阅一次,重新订阅3次 254 | assertEquals(mList, Arrays.asList(1, 2, 1, 2, 1, 2, 1, 2)); 255 | } 256 | 257 | } 258 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/FilteringOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | import com.geniusmart.rxjava.utils.OperatorUtils; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.List; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | import rx.Observable; 15 | import rx.Subscriber; 16 | import rx.schedulers.Schedulers; 17 | import rx.schedulers.TestScheduler; 18 | 19 | import static junit.framework.Assert.assertEquals; 20 | 21 | /** 22 | * Created by geniusmart on 2016/11/2. 23 | *

24 | * Operators that selectively emit items from a source Observable. 25 | */ 26 | public class FilteringOperatorsTest { 27 | 28 | private TestScheduler mTestScheduler; 29 | private List mList; 30 | 31 | @Before 32 | public void setUp() { 33 | mTestScheduler = new TestScheduler(); 34 | mList = new ArrayList<>(); 35 | } 36 | 37 | /** 38 | * only emit an item from an Observable if a particular timespan has passed without it 39 | * emitting another item 40 | *

41 | * Observable每产生一个结果后,如果在规定的间隔时间内没有别的结果产生,则把这个结果提交给订阅者处理,否则忽略该结果。 42 | * 43 | * @see RxMarbles diagrams debounce 44 | * @see ReactiveX operators 45 | * documentation: Debounce 46 | */ 47 | @Test 48 | public void debounce() { 49 | 50 | Observable.create(new Observable.OnSubscribe() { 51 | @Override 52 | public void call(Subscriber subscriber) { 53 | subscriber.onNext(1); 54 | OperatorUtils.sleep(500); 55 | 56 | subscriber.onNext(2); 57 | subscriber.onNext(3); 58 | subscriber.onNext(4); 59 | subscriber.onNext(5); 60 | 61 | OperatorUtils.sleep(500); 62 | subscriber.onNext(6); 63 | subscriber.onCompleted(); 64 | } 65 | }) 66 | .subscribeOn(mTestScheduler) 67 | .doOnNext(System.out::println) 68 | .debounce(400, TimeUnit.MILLISECONDS) 69 | .subscribe(mList::add); 70 | 71 | // 测试线程将时间提早10ms,可以保证create操作符顺利执行完毕 72 | mTestScheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS); 73 | System.out.println(mList); 74 | assertEquals(mList, Arrays.asList(1, 5, 6)); 75 | } 76 | 77 | /** 78 | * If the source Observable emits another item before this newly-generated Observable 79 | * terminates, debounce will suppress the item. 80 | *

81 | * 根据官方 marble diagrams 实现 82 | * 83 | * @see debounce.png 84 | */ 85 | @Test 86 | public void debounceWithSelector() { 87 | Observable.create(new Observable.OnSubscribe() { 88 | @Override 89 | public void call(Subscriber subscriber) { 90 | subscriber.onNext(1); 91 | OperatorUtils.sleep(2000); 92 | //2所对应的timer Observable还未结束,3已经开始发送,因此2将被废弃 93 | subscriber.onNext(2); 94 | subscriber.onNext(3); 95 | subscriber.onCompleted(); 96 | } 97 | }) 98 | .subscribeOn(mTestScheduler) 99 | .debounce(integer -> Observable.timer(1, TimeUnit.SECONDS)) 100 | .subscribe(mList::add); 101 | 102 | mTestScheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS); 103 | System.out.println(mList); 104 | assertEquals(mList, Arrays.asList(1, 3)); 105 | } 106 | 107 | /** 108 | * suppress duplicate items emitted by an Observable 109 | * 110 | * @see RxMarbles diagrams distinct 111 | */ 112 | @Test 113 | public void distinct() { 114 | Observable.just(1, 2, 2, 1, 3) 115 | .distinct() 116 | .subscribe(mList::add); 117 | assertEquals(mList, Arrays.asList(1, 2, 3)); 118 | } 119 | 120 | /** 121 | * @see RxMarbles diagrams 122 | * distinctUntilChanged 123 | */ 124 | @Test 125 | public void distinctUtilChange() { 126 | Observable.just(1, 2, 2, 1, 3) 127 | .distinctUntilChanged() 128 | .subscribe(mList::add); 129 | assertEquals(mList, Arrays.asList(1, 2, 1, 3)); 130 | } 131 | 132 | /** 133 | * emit only item n emitted by an Observable 134 | * 135 | * @see RxMarbles diagrams elementAt 136 | */ 137 | @Test 138 | public void elementAt() { 139 | Observable.just(1, 2, 3, 4) 140 | .elementAt(2) 141 | .subscribe(mList::add); 142 | assertEquals(mList, Collections.singletonList(3)); 143 | } 144 | 145 | /** 146 | * emit only those items from an Observable that pass a predicate test 147 | * 148 | * @see RxMarbles diagrams filter 149 | */ 150 | @Test 151 | public void filter() { 152 | Observable.just(2, 30, 22, 5, 60, 1) 153 | .filter(integer -> integer > 10) 154 | .subscribe(mList::add); 155 | assertEquals(mList, Arrays.asList(30, 22, 60)); 156 | } 157 | 158 | /** 159 | * find为RxPY操作符,这里使用filter+first来实现 160 | * 161 | * @see RxMarbles diagrams find 162 | */ 163 | @Test 164 | public void find() { 165 | Observable.just(2, 30, 22, 5, 60, 1) 166 | .filter(integer -> integer > 10) 167 | .first() 168 | .subscribe(mList::add); 169 | assertEquals(mList, Collections.singletonList(30)); 170 | } 171 | 172 | /** 173 | * emit only the first item, or the first item that meets a condition, from an Observable 174 | * 175 | * @see RxMarbles diagrams first 176 | */ 177 | @Test 178 | public void first() { 179 | Observable.just(1, 2, 3, 4) 180 | .first() 181 | .subscribe(mList::add); 182 | assertEquals(mList, Collections.singletonList(1)); 183 | } 184 | 185 | /** 186 | * do not emit any items from an Observable but mirror its termination notification 187 | * 188 | * @see ReactiveX 189 | * operators documentation: IgnoreElements 190 | */ 191 | @Test 192 | public void IgnoreElements() { 193 | Observable.just(1, 2, 3, 4, 5, 6) 194 | .ignoreElements() 195 | .doOnCompleted(() -> mList.add("Completed")) 196 | .subscribe(mList::add); 197 | assertEquals(mList, Collections.singletonList("Completed")); 198 | } 199 | 200 | 201 | /** 202 | * emit only the last item emitted by an Observable 203 | * 204 | * @see RxMarbles diagrams last 205 | */ 206 | @Test 207 | public void last() { 208 | Observable.just(1, 2, 3, 4) 209 | .last() 210 | .subscribe(mList::add); 211 | 212 | assertEquals(mList, Collections.singletonList(4)); 213 | } 214 | 215 | /** 216 | * suppress the first n items emitted by an Observable 217 | * 218 | * @see RxMarbles diagrams skip 219 | */ 220 | @Test 221 | public void skip() { 222 | Observable.just(1, 2, 3, 4) 223 | .skip(2) 224 | .subscribe(mList::add); 225 | assertEquals(mList, Arrays.asList(3, 4)); 226 | } 227 | 228 | /** 229 | * suppress the last n items emitted by an Observable 230 | * 231 | * @see RxMarbles diagrams skipLast 232 | */ 233 | @Test 234 | public void skipLast() { 235 | Observable.just(1, 2, 3, 4) 236 | .skipLast(2) 237 | .subscribe(mList::add); 238 | assertEquals(mList, Arrays.asList(1, 2)); 239 | } 240 | 241 | /** 242 | * emit only the first n items emitted by an Observable 243 | * 244 | * @see RxMarbles diagrams take 245 | */ 246 | @Test 247 | public void take() { 248 | Observable.just(1, 2, 3, 4) 249 | .take(2) 250 | .subscribe(mList::add); 251 | assertEquals(mList, Arrays.asList(1, 2)); 252 | } 253 | 254 | /** 255 | * emit only the last n items emitted by an Observable 256 | * 257 | * @see RxMarbles diagrams takeLast 258 | */ 259 | @Test 260 | public void takeLast() { 261 | Observable.just(1, 2, 3, 4) 262 | .takeLast(1) 263 | .subscribe(mList::add); 264 | assertEquals(mList, Collections.singletonList(4)); 265 | } 266 | 267 | /** 268 | * emit the most recent item emitted by an Observable within periodic time intervals 269 | * 270 | * @see RxMarbles diagrams sample 271 | * @see ReactiveX operators 272 | * documentation: Sample 273 | * @see RxJava wiki: 274 | * Backpressure 275 | */ 276 | @Test 277 | public void sample() { 278 | 279 | Observable observable1 = Observable.create(new Observable.OnSubscribe() { 280 | @Override 281 | public void call(Subscriber subscriber) { 282 | subscriber.onNext(1); 283 | OperatorUtils.sleep(500); 284 | subscriber.onNext(2); 285 | OperatorUtils.sleep(500); 286 | subscriber.onNext(3); 287 | OperatorUtils.sleep(500); 288 | subscriber.onNext(4); 289 | OperatorUtils.sleep(500); 290 | subscriber.onNext(5); 291 | OperatorUtils.sleep(500); 292 | subscriber.onCompleted(); 293 | } 294 | }) 295 | .subscribeOn(mTestScheduler) 296 | .doOnNext(System.out::println) 297 | .doOnCompleted(() -> System.out.println("observable1-Completed")); 298 | 299 | Observable observable2 = Observable.create(new Observable.OnSubscribe() { 300 | @Override 301 | public void call(Subscriber subscriber) { 302 | OperatorUtils.sleep(250); 303 | subscriber.onNext("A"); 304 | OperatorUtils.sleep(300); 305 | subscriber.onNext("B"); 306 | OperatorUtils.sleep(100); 307 | subscriber.onNext("C"); 308 | OperatorUtils.sleep(1000); 309 | subscriber.onNext("D"); 310 | OperatorUtils.sleep(500); 311 | subscriber.onCompleted(); 312 | } 313 | }) 314 | .subscribeOn(Schedulers.newThread()) 315 | .doOnNext(System.out::println) 316 | .doOnCompleted(() -> System.out.println("observable2-Completed")); 317 | 318 | 319 | observable1.sample(observable2) 320 | .subscribe(mList::add); 321 | 322 | mTestScheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS); 323 | System.out.println(mList); 324 | 325 | assertEquals(mList, Arrays.asList(1, 2, 4, 5)); 326 | } 327 | 328 | /** 329 | * Returns an Observable that emits only the first item emitted by the source Observable during 330 | * sequential time windows of a specified duration. 331 | *

332 | * 根据官方 marble diagrams 实现 333 | * 334 | * @see throttleFirst.png 335 | */ 336 | @Test 337 | public void throttleFirst() { 338 | Observable.create(new Observable.OnSubscribe() { 339 | @Override 340 | public void call(Subscriber subscriber) { 341 | subscriber.onNext(1); 342 | OperatorUtils.sleep(500); 343 | subscriber.onNext(2); 344 | subscriber.onNext(3); 345 | OperatorUtils.sleep(500); 346 | subscriber.onNext(4); 347 | subscriber.onNext(5); 348 | OperatorUtils.sleep(500); 349 | subscriber.onNext(6); 350 | subscriber.onCompleted(); 351 | } 352 | }) 353 | .subscribeOn(mTestScheduler) 354 | .doOnCompleted(() -> System.out.println("observable1-Completed")) 355 | .throttleFirst(500, TimeUnit.MILLISECONDS) 356 | .subscribe(mList::add); 357 | 358 | mTestScheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS); 359 | System.out.println(mList); 360 | assertEquals(mList, Arrays.asList(1, 2, 4, 6)); 361 | } 362 | 363 | /** 364 | * Returns an Observable that emits only the last item emitted by the source Observable during 365 | * sequential 366 | * time windows of a specified duration. 367 | *

368 | * 根据throttleFirst的官方 marble diagrams 实现 369 | */ 370 | @Test 371 | public void throttleLast() { 372 | Observable.create(new Observable.OnSubscribe() { 373 | @Override 374 | public void call(Subscriber subscriber) { 375 | subscriber.onNext(1); 376 | OperatorUtils.sleep(500); 377 | subscriber.onNext(2); 378 | subscriber.onNext(3); 379 | OperatorUtils.sleep(500); 380 | subscriber.onNext(4); 381 | subscriber.onNext(5); 382 | OperatorUtils.sleep(500); 383 | subscriber.onNext(6); 384 | subscriber.onCompleted(); 385 | } 386 | }) 387 | .subscribeOn(mTestScheduler) 388 | .doOnCompleted(() -> System.out.println("observable1-Completed")) 389 | .throttleLast(500, TimeUnit.MILLISECONDS) 390 | .subscribe(System.out::println); 391 | 392 | mTestScheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS); 393 | System.out.println(mList); 394 | assertEquals(mList, Arrays.asList(1, 3, 5, 6)); 395 | } 396 | 397 | } 398 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/MathematicalAndAggregateOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | import rx.Observable; 13 | import rx.observables.MathObservable; 14 | import rx.schedulers.TestScheduler; 15 | 16 | import static junit.framework.Assert.assertEquals; 17 | 18 | /** 19 | * Created by geniusmart on 16/11/2. 20 | *

21 | * Operators that operate on the entire sequence of items emitted by an Observable 22 | */ 23 | public class MathematicalAndAggregateOperatorsTest { 24 | 25 | private TestScheduler mTestScheduler; 26 | private List mList; 27 | 28 | @Before 29 | public void setUp() { 30 | mTestScheduler = new TestScheduler(); 31 | mList = new ArrayList<>(); 32 | } 33 | 34 | /** 35 | * calculates the average of numbers emitted by an Observable and emits this average 36 | * 37 | * @see RxMarbles diagrams average 38 | */ 39 | @Test 40 | public void average() { 41 | Observable observable = Observable.just(1, 2, 2, 2, 5); 42 | MathObservable.averageInteger(observable) 43 | .subscribe(mList::add); 44 | assertEquals(mList, Collections.singletonList(2)); 45 | 46 | mList.clear(); 47 | MathObservable.averageDouble(Observable.just(1.0, 2.0, 2.0, 2.0, 5.0)) 48 | .subscribe(mList::add); 49 | assertEquals(mList, Collections.singletonList(2.4)); 50 | } 51 | 52 | /** 53 | * emit the emissions from two or more Observables without interleaving them 54 | * 55 | * @see RxMarbles diagrams concat 56 | */ 57 | @Test 58 | public void concat() { 59 | Observable observable1 = Observable.interval(10, TimeUnit.SECONDS, mTestScheduler) 60 | .map(aLong -> 1) 61 | .take(3); 62 | 63 | Observable observable2 = Observable.interval(1, TimeUnit.SECONDS, mTestScheduler) 64 | .map(aLong -> 2) 65 | .take(2); 66 | 67 | Observable.concat(observable1, observable2) 68 | .subscribe(mList::add); 69 | 70 | mTestScheduler.advanceTimeBy(100, TimeUnit.SECONDS); 71 | 72 | assertEquals(mList, Arrays.asList(1, 1, 1, 2, 2)); 73 | } 74 | 75 | /** 76 | * count the number of items emitted by the source Observable and emit only this value 77 | * 78 | * @see RxMarbles diagrams count 79 | */ 80 | @Test 81 | public void count() { 82 | Observable.just(1, 2, 3, 4).count() 83 | .subscribe(mList::add); 84 | 85 | assertEquals(mList, Collections.singletonList(4)); 86 | } 87 | 88 | /** 89 | * determine, and emit, the maximum-valued item emitted by an Observable 90 | * 91 | * @see RxMarbles diagrams max 92 | */ 93 | @Test 94 | public void max() { 95 | MathObservable.max(Observable.just(2, 30, 22, 5, 60, 1)) 96 | .subscribe(mList::add); 97 | assertEquals(mList, Collections.singletonList(60)); 98 | } 99 | 100 | /** 101 | * determine, and emit, the minimum-valued item emitted by an Observable 102 | * 103 | * @see RxMarbles diagrams min 104 | */ 105 | @Test 106 | public void min() { 107 | MathObservable.min(Observable.just(2, 30, 22, 5, 60, 1)) 108 | .subscribe(mList::add); 109 | assertEquals(mList, Collections.singletonList(1)); 110 | } 111 | 112 | /** 113 | * apply a function to each item emitted by an Observable, sequentially, and emit the final 114 | * value 115 | * 116 | * @see RxMarbles diagrams reduce 117 | */ 118 | @Test 119 | public void reduce() { 120 | Observable.just(1, 2, 3, 4, 5) 121 | .reduce((num1, num2) -> num1 + num2) 122 | .subscribe(mList::add); 123 | assertEquals(mList, Collections.singletonList(15)); 124 | } 125 | 126 | /** 127 | * calculate the sum of numbers emitted by an Observable and emit this sum 128 | * 129 | * @see RxMarbles diagrams sum 130 | */ 131 | @Test 132 | public void sum() { 133 | MathObservable.sumInteger(Observable.just(1, 2, 3, 4, 5)) 134 | .subscribe(mList::add); 135 | assertEquals(mList, Collections.singletonList(15)); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/ToOperators.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | /** 4 | * Created by geniusmart on 16/12/7. 5 | *

6 | * convert an Observable into another object or data structure 7 | */ 8 | public class ToOperators { 9 | } 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/TransformingOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | import com.geniusmart.rxjava.utils.OperatorUtils; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import rx.Observable; 14 | import rx.Subscriber; 15 | import rx.functions.Func0; 16 | import rx.functions.Func1; 17 | import rx.schedulers.Schedulers; 18 | import rx.schedulers.TestScheduler; 19 | 20 | import static junit.framework.Assert.assertEquals; 21 | 22 | /** 23 | * Created by geniusmart on 2016/11/2. 24 | *

25 | * Operators that transform items that are emitted by an Observable. 26 | */ 27 | public class TransformingOperatorsTest { 28 | 29 | private TestScheduler mTestScheduler; 30 | private List mList; 31 | 32 | @Before 33 | public void setUp() { 34 | mTestScheduler = new TestScheduler(); 35 | mList = new ArrayList<>(); 36 | } 37 | 38 | /** 39 | * periodically gather items emitted by an Observable into bundles and emit these bundles 40 | * rather than emitting the items one at a time 41 | * 42 | * @see ReactiveX 43 | * operators documentation: Buffer 44 | */ 45 | @Test 46 | public void buffer() { 47 | 48 | Observable.just(1, 2, 3, 4, 5, 6) 49 | .buffer(3) 50 | .subscribe(mList::add); 51 | 52 | System.out.println(mList); 53 | List> exceptList = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6)); 54 | assertEquals(mList, exceptList); 55 | } 56 | 57 | /** 58 | * buffer(long,TimeUnit) 59 | * 60 | * @see 61 | * buffer diagram 62 | */ 63 | @Test 64 | public void bufferWithTimeUnit() { 65 | 66 | Observable.interval(0, 1, TimeUnit.SECONDS, mTestScheduler) 67 | .take(6) 68 | .buffer(2, TimeUnit.SECONDS, mTestScheduler) 69 | .subscribe(mList::add); 70 | 71 | 72 | mTestScheduler.advanceTimeBy(10, TimeUnit.SECONDS); 73 | System.out.println(mList); 74 | 75 | List> expectedList = Arrays.asList( 76 | Arrays.asList(0L, 1L), 77 | Arrays.asList(2L, 3L), 78 | Arrays.asList(4L, 5L)); 79 | assertEquals(mList, expectedList); 80 | } 81 | 82 | //TODO-bufferClosingSelector(是否准确待定) 83 | @Test 84 | public void bufferClosingSelector() { 85 | 86 | Observable observable = Observable.create(new Observable.OnSubscribe() { 87 | @Override 88 | public void call(Subscriber subscriber) { 89 | subscriber.onNext(1); 90 | subscriber.onNext(2); 91 | subscriber.onNext(3); 92 | OperatorUtils.sleep(2000); 93 | subscriber.onNext(4); 94 | subscriber.onNext(5); 95 | subscriber.onNext(6); 96 | OperatorUtils.sleep(500); 97 | } 98 | }) 99 | .subscribeOn(mTestScheduler) 100 | .doOnNext(System.out::println) 101 | .doOnCompleted(()->System.out.println("slkdfsd")); 102 | 103 | Observable bufferClosingSelector = Observable.create(new Observable.OnSubscribe() { 104 | @Override 105 | public void call(Subscriber subscriber) { 106 | OperatorUtils.sleep(1000); 107 | subscriber.onNext("close"); 108 | OperatorUtils.sleep(1500); 109 | subscriber.onNext("close"); 110 | subscriber.onCompleted(); 111 | 112 | } 113 | }) 114 | .subscribeOn(Schedulers.newThread()) 115 | .doOnNext(System.out::println); 116 | 117 | observable.buffer((Func0>) () -> bufferClosingSelector) 118 | .subscribe(mList::add); 119 | 120 | mTestScheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); 121 | 122 | System.out.println(mList); 123 | 124 | } 125 | 126 | /** 127 | * transform the items emitted by an Observable into Observables, then flatten the 128 | * emissions from those into a single Observable 129 | *

根据官方 marble diagrams 进行实现 130 | * 131 | * @see ReactiveX operators 132 | * documentation: FlatMap 133 | * @see faltMap.png 134 | */ 135 | @Test 136 | public void flatMap() { 137 | Observable.just(1, 2, 3) 138 | .flatMap((Func1>) num -> Observable.interval(num - 1, 139 | TimeUnit.SECONDS, mTestScheduler) 140 | .take(2) 141 | .map(value -> num + "◇")) 142 | .subscribe(mList::add); 143 | 144 | mTestScheduler.advanceTimeBy(100, TimeUnit.SECONDS); 145 | assertEquals(mList, Arrays.asList("1◇", "1◇", "2◇", "3◇", "2◇", "3◇")); 146 | System.out.println(mList); 147 | } 148 | 149 | /** 150 | * There is also a concatMap operator, which is like the simpler version of the flatMap 151 | * operator, but it concatenates rather than merges the resulting Observables in order to 152 | * generate its own sequence. 153 | *

根据官方 marble diagrams 进行实现 154 | *

155 | * concatMap与flatMap的区别在于,concatMap可以保持Observable发送时的顺序,而flatMap可能会交错的 156 | * 发送数据流 157 | * 158 | * @see concatMap.png 159 | */ 160 | @Test 161 | public void concatMap() { 162 | Observable.just(1, 2, 3) 163 | .concatMap((Func1>) num -> Observable.interval(num - 1, 164 | TimeUnit.SECONDS, mTestScheduler) 165 | .take(2) 166 | .map(value -> num + "◇")) 167 | .subscribe(mList::add); 168 | 169 | mTestScheduler.advanceTimeBy(100, TimeUnit.SECONDS); 170 | assertEquals(mList, Arrays.asList("1◇", "1◇", "2◇", "2◇", "3◇", "3◇")); 171 | System.out.println(mList); 172 | } 173 | 174 | /** 175 | * It behaves much like flatMap, except that whenever a new item is emitted by the source 176 | * Observable, it will unsubscribe to and stop mirroring the Observable that was generated 177 | * from the previously-emitted item, and begin only mirroring the current one. 178 | *

根据官方 marble diagrams 进行实现 179 | * 180 | * @see switchMap.png 181 | */ 182 | @Test 183 | public void switchMap() { 184 | 185 | Observable.create(new Observable.OnSubscribe() { 186 | @Override 187 | public void call(Subscriber subscriber) { 188 | subscriber.onNext(1); 189 | OperatorUtils.sleep(1500); 190 | subscriber.onNext(2); 191 | OperatorUtils.sleep(500); 192 | subscriber.onNext(3); 193 | OperatorUtils.sleep(1500); 194 | subscriber.onCompleted(); 195 | } 196 | }) 197 | .subscribeOn(mTestScheduler) 198 | .switchMap((Func1>) num -> 199 | Observable.interval(0, 1, TimeUnit.SECONDS) 200 | .take(2) 201 | .map(value -> num + "◇") 202 | ) 203 | .subscribe(mList::add); 204 | 205 | mTestScheduler.advanceTimeBy(1, TimeUnit.SECONDS); 206 | assertEquals(mList, Arrays.asList("1◇", "1◇", "2◇", "3◇", "3◇")); 207 | System.out.println(mList); 208 | } 209 | 210 | /** 211 | * divide an Observable into a set of Observables that each emit a different group of items 212 | * from the original Observable, organized by key 213 | * 214 | * @see ReactiveX operators 215 | * documentation: GroupBy 216 | */ 217 | @Test 218 | public void groupBy() { 219 | Observable.just(1, 2, 130, 3, 150, 999) 220 | .groupBy(num -> { 221 | 222 | // 根据规则产生键 223 | if (num > 100) { 224 | return "big"; 225 | } 226 | return "small"; 227 | }) 228 | .subscribe(groupedObservable -> { 229 | 230 | groupedObservable.subscribe(value -> { 231 | //通过getKey()可以获取键值 232 | String key = groupedObservable.getKey(); 233 | String result = key + "->" + value; 234 | System.out.println(result); 235 | mList.add(result); 236 | }); 237 | }); 238 | 239 | assertEquals(mList, Arrays.asList("small->1", "small->2", "big->130", "small->3", 240 | "big->150", "big->999")); 241 | 242 | } 243 | 244 | /** 245 | * transform the items emitted by an Observable by applying a function to each item 246 | * 247 | * @see RxMarbles map diagrams 248 | * @see ReactiveX operators 249 | * documentation: Map 250 | */ 251 | @Test 252 | public void map() { 253 | Observable.just(1, 2, 3) 254 | .map(integer -> integer * 10) 255 | .subscribe(mList::add); 256 | assertEquals(mList, Arrays.asList(10, 20, 30)); 257 | } 258 | 259 | /** 260 | * apply a function to each item emitted by an Observable, sequentially, and emit each 261 | * successive value 262 | * 263 | * @see RxMarbles scan diagrams 264 | * @see ReactiveX operators 265 | * documentation: Scan 266 | */ 267 | @Test 268 | public void scan() { 269 | 270 | Observable.just(1, 2, 3, 4, 5) 271 | .scan((num1, num2) -> num1 + num2) 272 | .subscribe(mList::add); 273 | assertEquals(mList, Arrays.asList(1, 3, 6, 10, 15)); 274 | 275 | } 276 | 277 | /** 278 | * periodically subdivide items from an Observable into Observable windows and emit 279 | * these windows rather than emitting the items one at a time 280 | * 281 | * @see ReactiveX 282 | * operators documentation: Window 283 | */ 284 | @Test 285 | public void window() { 286 | 287 | List list = new ArrayList<>(); 288 | 289 | Observable.just(1, 2, 3, 4, 5, 6) 290 | .window(3) 291 | .toBlocking() 292 | .forEach(list::add); 293 | 294 | for (int i = 0; i < 2; i++) { 295 | Observable observable = list.get(i); 296 | System.out.println(i + "->" + observable); 297 | observable.subscribe(System.out::println); 298 | } 299 | 300 | } 301 | 302 | } 303 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/UtilityOperatorsTest.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava; 2 | 3 | import com.geniusmart.rxjava.utils.OperatorUtils; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import rx.Observable; 14 | import rx.Subscriber; 15 | import rx.Subscription; 16 | import rx.functions.Action1; 17 | import rx.schedulers.Schedulers; 18 | import rx.schedulers.TestScheduler; 19 | 20 | import static com.geniusmart.rxjava.utils.OperatorUtils.logThread; 21 | import static junit.framework.Assert.assertEquals; 22 | import static junit.framework.Assert.assertTrue; 23 | 24 | /** 25 | * Created by geniusmart on 16/11/6. 26 | *

27 | * A toolbox of useful Operators for working with Observables 28 | */ 29 | public class UtilityOperatorsTest { 30 | 31 | private TestScheduler mTestScheduler; 32 | private List mList; 33 | 34 | @Before 35 | public void setUp() { 36 | mTestScheduler = new TestScheduler(); 37 | mList = new ArrayList<>(); 38 | } 39 | 40 | /** 41 | * shift the emissions from an Observable forward in time by a particular amount 42 | *

43 | * 此例子根据RxMarbles进行实现 44 | * 45 | * @see delay diagrams 46 | * @see ReactiveX operators 47 | * documentation: Delay 48 | */ 49 | @Test 50 | public void delay() { 51 | Observable.just(1, 2, 1) 52 | .delay(3000, TimeUnit.SECONDS, mTestScheduler) 53 | .subscribe(mList::add); 54 | 55 | mTestScheduler.advanceTimeBy(2000, TimeUnit.SECONDS); 56 | System.out.println("after 2000ms,result = " + mList); 57 | assertTrue(mList.isEmpty()); 58 | 59 | mTestScheduler.advanceTimeBy(1000, TimeUnit.SECONDS); 60 | System.out.println("after 3000ms,result = " + mList); 61 | assertEquals(mList, Arrays.asList(1, 2, 1)); 62 | 63 | } 64 | 65 | /** 66 | * shift the emissions from an Observable forward in time by a particular amount 67 | *

68 | * 此例子根据RxMarbles进行实现 69 | * 70 | * @see delayWithSelector diagrams 71 | */ 72 | @Test 73 | public void delayWithSelector() { 74 | 75 | Observable.just(1, 2, 1) 76 | .delay(integer -> Observable.timer(integer * 20, TimeUnit.SECONDS, mTestScheduler)) 77 | .subscribe(mList::add); 78 | 79 | mTestScheduler.advanceTimeTo(20, TimeUnit.SECONDS); 80 | assertEquals(mList, Arrays.asList(1, 1)); 81 | 82 | mTestScheduler.advanceTimeTo(40, TimeUnit.SECONDS); 83 | assertEquals(mList, Arrays.asList(1, 1, 2)); 84 | } 85 | 86 | /** 87 | * Returns an Observable that delays the subscription to the source Observable by a given 88 | * amount of time. 89 | * 90 | * @see ReactiveX operators 91 | * documentation: Delay 92 | */ 93 | @Test 94 | public void delaySubscription() { 95 | 96 | //延时5s订阅 97 | Observable.just(888) 98 | .delaySubscription(5, TimeUnit.SECONDS, mTestScheduler) 99 | .doOnSubscribe(() -> System.out.println("o1->doOnSubscribe")) 100 | .doOnNext(System.out::println) 101 | .subscribe(mList::add); 102 | 103 | //延时2s订阅,此数据流会先被订阅 104 | Observable.just(666) 105 | .delaySubscription(2, TimeUnit.SECONDS, mTestScheduler) 106 | .doOnSubscribe(() -> System.out.println("o2->doOnSubscribe")) 107 | .doOnNext(System.out::println) 108 | .subscribe(mList::add); 109 | 110 | mTestScheduler.advanceTimeBy(10, TimeUnit.SECONDS); 111 | assertEquals(mList, Arrays.asList(666, 888)); 112 | } 113 | 114 | /** 115 | * register an action to take upon a variety of Observable lifecycle events 116 | * 117 | * @see ReactiveX operators 118 | * documentation: Do 119 | */ 120 | @Test 121 | public void doOnSubscribe_doOnNext_doOnCompleted__doOnUnsubscribe() { 122 | Subscription subscribe = Observable.just(1, 2, 3) 123 | .doOnCompleted(() -> mList.add("doOnCompleted")) 124 | .doOnNext(mList::add) 125 | .doOnSubscribe(() -> mList.add("doOnSubscribe")) 126 | .doOnUnsubscribe(() -> mList.add("doOnUnsubscribe")) 127 | .subscribe(); 128 | 129 | subscribe.unsubscribe(); 130 | 131 | assertEquals(mList, Arrays.asList("doOnSubscribe", 1, 2, 3, "doOnCompleted", "doOnUnsubscribe")); 132 | 133 | } 134 | 135 | //TODO-doOnRequest-参考http://blog.chengyunfeng.com/?p=981 136 | @Test 137 | public void doOnRequest() { 138 | Observable.just(1, 2, 3, 4, 5, 6) 139 | .doOnRequest(new Action1() { 140 | @Override 141 | public void call(Long aLong) { 142 | } 143 | }); 144 | } 145 | 146 | /** 147 | * register an action to take upon a variety of Observable lifecycle events 148 | * 149 | * @see ReactiveX operators 150 | * documentation: Do 151 | */ 152 | @Test 153 | public void doOnEach_doOnError() { 154 | Observable.create(new Observable.OnSubscribe() { 155 | @Override 156 | public void call(Subscriber subscriber) { 157 | subscriber.onNext(1); 158 | subscriber.onNext(5 / 0); 159 | } 160 | }) 161 | .doOnEach(notification -> { 162 | String actionName = notification.getKind().name(); 163 | Object value = notification.getValue(); 164 | System.out.println("doOnEach--" + actionName + "->" + value); 165 | }) 166 | .doOnError(throwable -> System.out.println("doOnError->" + throwable.getMessage())) 167 | .subscribe( 168 | num -> { 169 | System.out.println("subscribe--" + num); 170 | }, 171 | throwable -> { 172 | System.out.println("subscribe--" + throwable.getMessage()); 173 | } 174 | ); 175 | } 176 | 177 | /** 178 | * registers an Action which will be called just before the resulting Observable terminates, 179 | * whether normally or with an error. 180 | * 181 | * @see ReactiveX operators 182 | * documentation: Do 183 | */ 184 | @Test 185 | public void doOnTerminate() { 186 | Observable.just(1) 187 | .doOnTerminate(() -> mList.add("doOnTerminate by Completed")) 188 | .subscribe(); 189 | 190 | Observable.create(subscriber -> { 191 | subscriber.onError(new Exception("null")); 192 | }) 193 | .doOnTerminate(() -> mList.add("doOnTerminate by Error")) 194 | .subscribe( 195 | num -> { 196 | }, 197 | throwable -> { 198 | }); 199 | 200 | assertEquals(mList, Arrays.asList("doOnTerminate by Completed", "doOnTerminate by Error")); 201 | } 202 | 203 | /** 204 | * The doAfterTerminate operator registers an Action which will be called just after the 205 | * resulting Observable terminates, whether normally or with an error. 206 | * 207 | * @see ReactiveX operators 208 | * documentation: Do 209 | */ 210 | @Test 211 | public void doAfterTerminate() { 212 | Observable.just(1, 2, 3, 4, 5, 6) 213 | .doAfterTerminate(() -> mList.add("doAfterTerminate")) 214 | .subscribe(mList::add); 215 | 216 | assertEquals(mList, Arrays.asList(1, 2, 3, 4, 5, 6, "doAfterTerminate")); 217 | } 218 | 219 | /** 220 | * represent both the items emitted and the notifications sent as emitted items, or reverse 221 | * this process 222 | * 223 | * @see ReactiveX 224 | * operators documentation: Materialize 225 | */ 226 | @Test 227 | public void materialize() { 228 | Observable.just(1, 2) 229 | .materialize() 230 | .doOnNext(System.out::println) 231 | .subscribe(notification -> { 232 | mList.add(notification.getKind().name() + "->" + notification.getValue()); 233 | }); 234 | assertEquals(mList, Arrays.asList("OnNext->1", "OnNext->2", "OnCompleted->null")); 235 | } 236 | 237 | /** 238 | * represent both the items emitted and the notifications sent as emitted items, or reverse 239 | * this process 240 | * 241 | * @see ReactiveX 242 | * operators documentation: Materialize 243 | */ 244 | @Test 245 | public void dematerialize() { 246 | Observable.just(1, 2) 247 | .materialize() 248 | .doOnNext(System.out::println) 249 | .dematerialize() 250 | .doOnNext(System.out::println) 251 | .subscribe(mList::add); 252 | assertEquals(mList, Arrays.asList(1, 2)); 253 | } 254 | 255 | /** 256 | * specify the scheduler on which an observer will observe this Observable 257 | * 258 | * @see ReactiveX operators 259 | * documentation: ObserveOn 260 | */ 261 | @Test 262 | public void observeOn() { 263 | Observable.create(new Observable.OnSubscribe() { 264 | @Override 265 | public void call(Subscriber subscriber) { 266 | OperatorUtils.logThread("Observable"); 267 | subscriber.onNext(1); 268 | subscriber.onCompleted(); 269 | } 270 | }) 271 | .observeOn(Schedulers.newThread()) 272 | .subscribe((num) -> { 273 | OperatorUtils.logThread("Subscriber"); 274 | }); 275 | } 276 | 277 | /** 278 | * TODO-serialize 279 | * force an Observable to make serialized calls and to be well-behaved 280 | * 281 | * @see ReactiveX operators 282 | * documentation: Serialize 283 | */ 284 | @Test 285 | public void serialize() { 286 | 287 | } 288 | 289 | /** 290 | * operate upon the emissions and notifications from an Observable 291 | * 292 | * @see ReactiveX operators 293 | * documentation: Subscribe 294 | */ 295 | @Test 296 | public void subscribe() { 297 | Observable.just(1, 2, 3) 298 | .subscribe(mList::add, 299 | (throwable) -> mList.add("Error"), 300 | () -> mList.add("Complete")); 301 | assertEquals(mList, Arrays.asList(1, 2, 3, "Complete")); 302 | } 303 | 304 | /** 305 | * specify the scheduler an Observable should use when it is subscribed to 306 | * 307 | * @see ReactiveX 308 | * operators documentation: SubscribeOn 309 | */ 310 | @Test 311 | public void subscribeOn() { 312 | Observable.create(new Observable.OnSubscribe() { 313 | @Override 314 | public void call(Subscriber subscriber) { 315 | logThread("Observable"); 316 | subscriber.onNext(1); 317 | subscriber.onCompleted(); 318 | } 319 | }) 320 | .subscribeOn(Schedulers.newThread()) 321 | .subscribe((num) -> { 322 | logThread("Subscriber"); 323 | }); 324 | } 325 | 326 | /** 327 | * 根据官方 marble diagrams 实现 328 | * 329 | * @see schedulers.png 330 | */ 331 | @Test 332 | public void subscribeOn_and_observeOn() { 333 | Observable.create(new Observable.OnSubscribe() { 334 | @Override 335 | public void call(Subscriber subscriber) { 336 | logThread("Observable"); 337 | subscriber.onNext(1); 338 | subscriber.onCompleted(); 339 | } 340 | }) 341 | .observeOn(Schedulers.io())//决定了map()的线程 342 | .map(num -> { 343 | logThread("map"); 344 | return num; 345 | }) 346 | .subscribeOn(Schedulers.newThread())//决定了消息源的线程 347 | .observeOn(Schedulers.computation())//决定了订阅者的线程 348 | .subscribe((num) -> { 349 | logThread("Subscriber"); 350 | }); 351 | 352 | //保证所有线程正常执行完毕 353 | OperatorUtils.sleep(1000); 354 | } 355 | 356 | /** 357 | * convert an Observable that emits items into one that emits indications of the amount of 358 | * time elapsed between those emissions 359 | * 360 | * @see ReactiveX 361 | * operators documentation: TimeInterval 362 | */ 363 | @Test 364 | public void timeInterval() { 365 | Observable.create(new Observable.OnSubscribe() { 366 | @Override 367 | public void call(Subscriber subscriber) { 368 | OperatorUtils.sleep(500); 369 | subscriber.onNext(1); 370 | OperatorUtils.sleep(1000); 371 | subscriber.onNext(2); 372 | OperatorUtils.sleep(2000); 373 | subscriber.onNext(3); 374 | OperatorUtils.sleep(3000); 375 | subscriber.onCompleted(); 376 | } 377 | }) 378 | .take(5) 379 | .timeInterval() 380 | .subscribe(System.out::println); 381 | } 382 | 383 | /** 384 | * mirror the source Observable, but issue an error notification if a particular period of 385 | * time elapses without any emitted items 386 | * 387 | * @see ReactiveX operators 388 | * documentation: Timeout 389 | */ 390 | @Test 391 | public void timeout() { 392 | Observable.create(new Observable.OnSubscribe() { 393 | @Override 394 | public void call(Subscriber subscriber) { 395 | subscriber.onNext(1); 396 | subscriber.onNext(2); 397 | subscriber.onNext(3); 398 | subscriber.onNext(4); 399 | OperatorUtils.sleep(3000); 400 | subscriber.onNext(5); 401 | subscriber.onCompleted(); 402 | } 403 | }) 404 | .subscribeOn(mTestScheduler) 405 | .timeout(2, TimeUnit.SECONDS) 406 | .doOnError(System.out::println) 407 | .subscribe(num -> { 408 | mList.add(num); 409 | }, throwable -> { 410 | mList.add("throwable"); 411 | }); 412 | 413 | mTestScheduler.advanceTimeBy(1, TimeUnit.SECONDS); 414 | assertEquals(mList, Arrays.asList(1, 2, 3, 4, "throwable")); 415 | } 416 | 417 | /** 418 | * attach a timestamp to each item emitted by an Observable indicating when it was emitted 419 | * 420 | * @see ReactiveX operators 421 | * documentation: Timestamp 422 | */ 423 | @Test 424 | public void timestamp() { 425 | System.out.println("start=" + System.currentTimeMillis()); 426 | Observable.create(new Observable.OnSubscribe() { 427 | @Override 428 | public void call(Subscriber subscriber) { 429 | subscriber.onNext(1); 430 | OperatorUtils.sleep(1000); 431 | subscriber.onNext(2); 432 | OperatorUtils.sleep(3000); 433 | subscriber.onNext(3); 434 | subscriber.onCompleted(); 435 | } 436 | }) 437 | .timestamp() 438 | .subscribe(System.out::println); 439 | System.out.println("end" + System.currentTimeMillis()); 440 | } 441 | 442 | /** 443 | * TODO-using 444 | * create a disposable resource that has the same lifespan as the Observable 445 | * 446 | * @see ReactiveX operators 447 | * documentation: Using 448 | */ 449 | @Test 450 | public void using() { 451 | 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/utils/OperatorUtils.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava.utils; 2 | 3 | /** 4 | * Created by geniusmart on 2016/11/9. 5 | */ 6 | public class OperatorUtils { 7 | 8 | public static void sleep(long time) { 9 | try { 10 | Thread.sleep(time); 11 | } catch (InterruptedException e) { 12 | e.printStackTrace(); 13 | } 14 | } 15 | 16 | public static void logThread(String name){ 17 | System.out.println(name + " from " + Thread.currentThread().getName()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/test/java/com/geniusmart/rxjava/utils/ThreadTheory.java: -------------------------------------------------------------------------------- 1 | package com.geniusmart.rxjava.utils; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import rx.Observable; 8 | import rx.Subscriber; 9 | import rx.schedulers.Schedulers; 10 | import rx.schedulers.TestScheduler; 11 | 12 | /** 13 | * Created by geniusmart on 2016/11/10. 14 | * 测试原理介绍 15 | */ 16 | public class ThreadTheory { 17 | 18 | /** 19 | * 测试线程早于子线程执行完毕 20 | */ 21 | @Test 22 | public void test_thread_early() { 23 | 24 | //测试线程启动 25 | System.out.println("测试线程-start"); 26 | 27 | new Thread(new Runnable() { 28 | @Override 29 | public void run() { 30 | System.out.println("子线程-start"); 31 | OperatorUtils.sleep(3000); 32 | System.out.println("子线程-end"); 33 | } 34 | }).start(); 35 | 36 | //测试线程结束后,子线程还未执行完毕,因此子线程无法完整的输出测试结果 37 | System.out.println("测试线程-end"); 38 | } 39 | 40 | /** 41 | * 测试线程晚于子线程执行完毕 42 | */ 43 | @Test 44 | public void test_thread_late() { 45 | 46 | //测试线程启动,线程体需执行4s钟 47 | System.out.println("测试线程-start"); 48 | 49 | //子线程能顺利执行完毕 50 | new Thread(new Runnable() { 51 | @Override 52 | public void run() { 53 | System.out.println("子线程-start"); 54 | OperatorUtils.sleep(3000); 55 | System.out.println("子线程-end"); 56 | } 57 | }).start(); 58 | 59 | OperatorUtils.sleep(4000); 60 | System.out.println("测试线程-end"); 61 | } 62 | 63 | /** 64 | * 测试线程早于子线程执行完毕 65 | */ 66 | @Test 67 | public void test_thread_early_observable() { 68 | System.out.println("测试线程-start,所在线程:" + Thread.currentThread().getName()); 69 | 70 | //消息源在Schedulers.computation()线程中执行,3s后执行,此时测试线程已经执行完毕,无法正常输出结果 71 | Observable.timer(3, TimeUnit.SECONDS) 72 | .subscribe(num -> { 73 | System.out.println("Observable和Subscriber所在线程:" + Thread.currentThread().getName()); 74 | System.out.println("获取订阅数据:" + num); 75 | 76 | }); 77 | System.out.println("测试线程-end"); 78 | } 79 | 80 | /** 81 | * 让测试线程晚于子线程执行完毕 82 | */ 83 | @Test 84 | public void test_thread_late_observable() { 85 | 86 | System.out.println("测试线程-start,所在线程:" + Thread.currentThread().getName()); 87 | //消息源在Schedulers.computation()线程中执行,3s后执行 88 | Observable.timer(3, TimeUnit.SECONDS) 89 | .subscribe(num -> { 90 | System.out.println("Observable和Subscriber线程:" + Thread.currentThread().getName()); 91 | System.out.println("获取订阅数据:" + num); 92 | }); 93 | 94 | // 测试线程在4s后结束,能保证子线程完整执行 95 | OperatorUtils.sleep(4000); 96 | System.out.println("测试线程-end"); 97 | } 98 | 99 | /** 100 | * 使用TestScheduler将线程设置为测试线程,并将时间提前 101 | */ 102 | @Test 103 | public void test_thread_with_TestScheduler() { 104 | 105 | TestScheduler testScheduler = Schedulers.test(); 106 | System.out.println("测试线程:" + Thread.currentThread().getName()); 107 | 108 | //指定调度器 109 | Observable.timer(3, TimeUnit.SECONDS, testScheduler) 110 | .subscribe(num -> { 111 | System.out.println("Observable和Subscriber线程:" + Thread.currentThread().getName()); 112 | System.out.println("获取订阅数据:" + num); 113 | }); 114 | 115 | //将时间提前了3s 116 | testScheduler.advanceTimeBy(3, TimeUnit.SECONDS); 117 | } 118 | 119 | /** 120 | * 聚合操作符的线程处理方式一:两个Observable分别在两条子线程中执行,且测试线程的生命周期比2条子线程更长 121 | */ 122 | @Test 123 | public void combiningOperator_thread_way1() { 124 | 125 | //observable1在子线程1中执行 126 | Observable observable1 = Observable.create(new Observable.OnSubscribe() { 127 | @Override 128 | public void call(Subscriber subscriber) { 129 | System.out.println("observable1-->" + Thread.currentThread().getName()); 130 | subscriber.onNext(1); 131 | OperatorUtils.sleep(500); 132 | subscriber.onNext(2); 133 | OperatorUtils.sleep(1500); 134 | subscriber.onNext(3); 135 | OperatorUtils.sleep(250); 136 | subscriber.onNext(4); 137 | OperatorUtils.sleep(500); 138 | subscriber.onNext(5); 139 | subscriber.onCompleted(); 140 | } 141 | }).subscribeOn(Schedulers.newThread()); 142 | 143 | //observable2在子线程2中执行 144 | Observable observable2 = Observable.create(new Observable.OnSubscribe() { 145 | @Override 146 | public void call(Subscriber subscriber) { 147 | OperatorUtils.sleep(200); 148 | System.out.println("observable2-->" + Thread.currentThread().getName()); 149 | subscriber.onNext(1111); 150 | subscriber.onNext(2222); 151 | subscriber.onNext(3333); 152 | subscriber.onCompleted(); 153 | } 154 | }).subscribeOn(Schedulers.newThread()); 155 | 156 | Observable.merge(observable1, observable2).subscribe(System.out::println); 157 | 158 | //测试线程休眠一定时间,保证两个消息源所在线程能正常执行完毕 159 | OperatorUtils.sleep(5000); 160 | 161 | } 162 | 163 | /** 164 | * 聚合操作符的线程处理方式二:一个Observable在TestScheduler线程中执行,另外一个Observable在子线程中 165 | * 执行,并将时钟提前,保证Observable1能顺利执行完毕 166 | */ 167 | @Test 168 | public void combiningOperator_thread_way2() { 169 | 170 | TestScheduler testScheduler = new TestScheduler(); 171 | 172 | Observable observable = Observable.create(new Observable.OnSubscribe() { 173 | @Override 174 | public void call(Subscriber subscriber) { 175 | System.out.println("observable1-->" + Thread.currentThread().getName()); 176 | subscriber.onNext(1); 177 | OperatorUtils.sleep(500); 178 | subscriber.onNext(2); 179 | OperatorUtils.sleep(1500); 180 | subscriber.onNext(3); 181 | OperatorUtils.sleep(250); 182 | subscriber.onNext(4); 183 | OperatorUtils.sleep(500); 184 | subscriber.onNext(5); 185 | subscriber.onCompleted(); 186 | } 187 | }).subscribeOn(testScheduler); 188 | 189 | Observable observable2 = Observable.create(new Observable.OnSubscribe() { 190 | @Override 191 | public void call(Subscriber subscriber) { 192 | OperatorUtils.sleep(200); 193 | System.out.println("observable2-->" + Thread.currentThread().getName()); 194 | subscriber.onNext(1111); 195 | subscriber.onNext(2222); 196 | subscriber.onNext(3333); 197 | subscriber.onCompleted(); 198 | } 199 | }).subscribeOn(Schedulers.newThread()); 200 | 201 | Observable.merge(observable, observable2).subscribe(System.out::println); 202 | 203 | testScheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); 204 | 205 | } 206 | 207 | } 208 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geniusmart/RxJavaOperatorsUTSample/d72d29e9d6f6f93ef82137275f20a43220414723/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------