花了一段时间学习rxJava,有关于1.x版本的和2.x版本的,主要看的还是2.x版本。
下面将我主要看的两篇文章进行了整合,除了相当于为自己整合一下,也可以让我更加熟悉里面一些操作的用法。
两篇文章的原地址分别是:
1、这可能是最好的RxJava 2.x 入门教程系列(主要讲的RxJava2.x)
2、给 Android 开发者的 RxJava 详解(主要讲的RxJava1.x)
有兴趣的小伙伴可以直接去看,写的都非常非常好!
不过两篇写的太多了,我目前只整理了一部分,后续会完成全部的。
操作符表格
操作符 | 原文地址 | 作用 |
create | 用于产生一个被观察者Obserable对象(Obserable为上游的发射器,Observer为下游接收器) | |
map | 对发射器发送的每一个事件应用一个函数,每一个事件都按照指定的函数去变化 | |
zip | 专用于合并事件,该合并不是连接,而是两两配对,也就意味着,最终配对出的 Observable 发射事件数目只和少的那个相同 | |
concat | 对于单一的把两个发射器连接成一个发射器 | |
FlatMap | 它可以把一个发射器 Observable 通过某种方法转换为多个 Observables,然后再把这些分散的 Observables装进一个单一的发射器 Observable。(不过不保证事件的顺序) | |
concatMap | concatMap 与 FlatMap 的唯一区别就是 concatMap 保证了顺序 | |
distinct | distinct操作符的作用就是去重 | |
Filter | 过滤器,可以接受一个参数,让其过滤掉不符合我们条件的值 | |
buffer | buffer 操作符接受两个参数,buffer(count,skip),作用是将 Observable 中的数据按 skip (步长) 分成最大不超过 count 的 buffer ,然后生成一个 Observable | |
timer | timer 很有意思,相当于一个定时任务。在 1.x 中它还可以执行间隔逻辑,但在 2.x 中此功能被交给了 interval。但需要注意的是,timer 和 interval 均默认在新线程 | |
interval | interval 操作符用于间隔时间执行某个操作,其接受三个参数,分别是第一次发送延迟,间隔时间,时间单位 | |
doOnNext | 它的作用是让订阅者在接收到数据之前干点其他的事情 | |
skip | skip 作用就和字面意思一样,接受一个long 型参数 count ,代表跳过 count 个数目开始接收 | |
take | 接受一个 long 型参数 count ,代表至多接收 count 个数据 | |
just | 是一个简单的发射器依次调用 onNext() 方法 | |
Single | Single 只会接收一个参数,而 SingleObserver 只会调用 onError() 或者 onSuccess() | |
debounce | 去除发送频率过快的项,用处很大 | |
defer | 简单地时候就是每次订阅都会创建一个新的 Observable,并且如果没有被订阅,就不会产生新的 Observable。 | |
last | last 操作符仅取出可观察到的最后一个值,或者是满足某些条件的最后一项 | |
merge | merge 的作用是把多个 Observable 结合起来,接受可变参数,也支持迭代器集合。它和concat 的区别在于,不用等到发射器 A 发送完所有的事件再进行发射器B的发送 | |
reduce | reduce 操作符每次用一个方法处理一个值,可以有一个 seed 作为初始值 | |
scan | scan 操作符作用和上面的 reduce 一致,唯一区别是 reduce 只追求结果,而 scan 会把每一个步骤都输出 | |
window | 按照实际划分窗口,将数据发送给不同的 Observable |
create
create 是比较常见的操作符,可以直接通过它创建一个 Observable ,你可以传递这个操作符给可以接受 observe 作为参数的函数。
一个符合规定的 Observable 必须尝试去调用 observe 的 onComplete 方法或是 onError 方法,并且之后不能再调用 observe 的任何其他方法。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
Log.e(TAG, "Observable发射了 1 \n");
emitter.onNext(1);
Log.e(TAG, "Observable发射了 2 \n");
emitter.onNext(2);
Log.e(TAG, "Observable发射了 3 \n");
emitter.onNext(3);
Log.e(TAG, "Observable发射了 5 \n");
emitter.onNext(5);
}
}).subscribe(new Observer<Integer>() {
private int i;
private Disposable mDisposable;
@Override
public void onSubscribe(Disposable d) {
Log.e(TAG, "onSubscribe是否被切断订阅?" + d.isDisposed());
mDisposable = d;
}
@Override
public void onNext(Integer integer) {
Log.e(TAG, "onNext执行的参数值为:" + integer);
i++;
if (i >= 3){
mDisposable.dispose();
Log.e(TAG, "onNext是否被切断执行?" + mDisposable.isDisposed());
}
}
@Override
public void onError(Throwable e) {
Log.e(TAG, e.getMessage());
}
@Override
public void onComplete() {
Log.e(TAG, "完成!");
}
});
输出:
有几点需要注意:
- 在发射事件中,我们在发射了数值 3 之后,直接调用了 e.onComlete(),虽然无法接收事件,但发送事件还是继续的。
- 另外一个值得注意的点是,在 RxJava 2.x 中,可以看到发射事件方法相比 1.x 多了一个 throws Excetion,意味着我们做一些特定操作再也不用 try-catch 了。
- 并且 2.x 中有一个 Disposable 概念,这个东西可以直接调用切断,可以看到,当它的 isDisposed() 返回为 false 的时候,接收器能正常接收事件,但当其为 true 的时候,接收器停止了接收。所以可以通过此参数动态控制接收事件了。
点击返回表格
Map
通过应用某个函数去转换被 Observable 发射的各项事件。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) throws Exception {
return "apply——" + integer;
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, "转化结果是:" + s);
}
});
输出:
是的,map 基本作用就是将一个 Observable 通过某种函数关系,转换为另一种 Observable,上面例子中就是把我们的 Integer 数据变成了 String 类型,其中,apply为转换函数,accept方法中的s为转换结果。可以从Log日志看出。
点击返回表格
Zip
zip 专用于合并事件,该合并不是连接(连接操作符后面会说),而是两两配对,也就意味着,最终配对出的 Observable 发射事件数目是取分支上数目较少的那些。
private void rxJavaTestForZip(){
Observable.zip(getStringObservable(), getIntegerObservable(), new BiFunction<String, Integer, String>() {
@Override
public String apply(String s, Integer integer) throws Exception {
return s + integer;
}
}).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, "zip的accept是:" + s);
}
});
}
private Observable<String> getStringObservable() {
return Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
if (!emitter.isDisposed()){
emitter.onNext("A");
Log.e(TAG, "发射的String是:A\n");
emitter.onNext("B");
Log.e(TAG, "发射的String是:B\n");
emitter.onNext("C");
Log.e(TAG, "发射的String是:C\n");
}
}
});
}
private Observable<Integer> getIntegerObservable(){
return Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
if (!emitter.isDisposed()){
emitter.onNext(1);
Log.e(TAG, "发射的Integer是:1\n");
emitter.onNext(2);
Log.e(TAG, "发射的Integer是:2\n");
emitter.onNext(3);
Log.e(TAG, "发射的Integer是:3\n");
emitter.onNext(4);
Log.e(TAG, "发射的Integer是:4\n");
}
}
});
}
输出:
需要注意的是:
- Zip 组合事件的过程就是分别从发射器 A 和发射器 B 各取出一个事件来组合,并且一个事件只能被使用一次,组合的顺序是严格按照事件发送的顺序来进行的,所以上面截图中,可以看到,1 永远是和 A 结合的,2 永远是和 B 结合的。
- 最终接收器收到的事件数量是和发送器发送事件最少的那个发送器的发送事件数目相同,所以如截图中,4 很孤单,没有人愿意和它交往,孤独终老的单身狗。
点击返回表格
Concat
对于单一的把两个发射器连接成一个发射器,虽然 zip 不能完成,但我们还是可以自力更生,官方提供的 concat 让我们的问题得到了完美解决.
Observable.concat(Observable.just("学", "粤", "语"), Observable.just("吼", "难", "哇"))
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, "Concat后的结果是:" + s);
}
});
输出:
如图,可以看到。发射器 B 把自己的三个孩子送给了发射器 A,让他们组合成了一个新的发射器,这些事件的顺序都是固定的。
点击返回表格
FlatMap
FlatMap 可以把一个发射器 Observable 通过某种方法转换为多个 Observables,然后再把这些分散的 Observables 装进一个单一的发射器 Observable。但有个需要注意的是,FlatMap 并不能保证事件的顺序,如果需要保证,需要用到下面要讲的 ConcatMap。
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("这");
emitter.onNext("段");
emitter.onNext("话");
}
}).flatMap(new Function<String, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(String s) throws Exception {
List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("value是:" + s);
}
int delayTime = (int) (1 + Math.random() * 10);
return Observable.fromIterable(list).delay(delayTime, TimeUnit.MILLISECONDS);
}
}).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())//此处的AndroidSchedulers来自于rxAndroid
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, "FlatMap后结果为:" + s);
}
});
输出:
一切都如我们预期中的有意思,为了区分 concatMap(下一个会讲),我在代码中特意动了一点小手脚,我采用一个随机数,生成一个时间,然后通过 delay(后面会讲)操作符,做一个小延时操作,而查看 Log 日志也确认验证了我们上面的说法,它是无序的。
点击返回表格
concatMap
concatMap 和 FlatMap 最大的唯一区别就是 concatMap 保证了顺序,所以可以直接把 FlatMap 替换为 concatMap 进行验证。
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("这");
emitter.onNext("段");
emitter.onNext("话");
}
}).concatMap(new Function<String, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(String s) throws Exception {
List<String> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
list.add("value是:" + s);
}
int delayTime = (int) (1 + Math.random() * 10);
return Observable.fromIterable(list).delay(delayTime, TimeUnit.MILLISECONDS);
}
}).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, "FlatMap后结果为:" + s);
}
});
输出:
结果的确和我们预想的一样。
点击返回表格
distinct
去除掉从Observable发射的重复事件
Distinct 操作符通过仅允许发送未曾发送过的事件的方式去过滤一个 Observable
Observable.just("说","说","粤","语","语", "说")
.distinct()
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, "Distinct后的结果是:" + s);
}
});
输出:
显然,Log日志的结果在想象之中。
点击返回表格
Filter
只能发射那些通过一个predicate过滤测试的事件
Filter 操作符通过在 predicate 中你所指定的判定函数去过滤一个 Observable
Observable.just(1,1,2,3,4,5,6,9,0)
.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) throws Exception {
return integer == 2 || integer == 5 || integer == 0;
}
})
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "Filter过滤后的结果为:" + integer);
}
});
输出:
过滤条件是只接受值为2、5、0的数,所以输出是这样。
点击返回表格
buffer
周期性地将Observable发射的事件收集到bundles, 并且发射这些bundles而不是一次只发一个
Buffer 操作符转换一个发射事件的 Observalbe 为另一个发射收集了那些事件的 buffers 的 Observable
buffer 操作符接受两个参数,buffer(count,skip),作用是将 Observable 中的数据按 skip (步长) 分成最大不超过 count 的 buffer ,然后生成一个 Observable 。也许你还不太理解,我们看一下多种情况就知道了。
Observable.just(1,2,3,4,5,6,7)
.buffer(3,2)
.subscribe(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> integers) throws Exception {
Log.e(TAG, "buffer长度是: " + integers.size());
for (Integer i : integers){
Log.e(TAG, "buffer中的值是:" + i);
}
}
});
输出:
Observable.just(1,2,3,4,5,6,7)
.buffer(3)
.subscribe(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> integers) throws Exception {
Log.e(TAG, "buffer长度是: " + integers.size());
}
});
输出:
Observable.just(1,2,3,4,5,6,7)
.buffer(3,4)
.subscribe(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> integers) throws Exception {
Log.e(TAG, "buffer长度是: " + integers.size());
for (Integer i : integers){
Log.e(TAG, "buffer中的值是:" + i);
}
}
});
输出:
看了这几种情况,我觉得应该够解释前面的那段话了。
点击返回表格
timer
timer 很有意思,相当于一个定时任务。在 1.x 中它还可以执行间隔逻辑,但在 2.x 中此功能被交给了 interval,下一个会介绍。但需要注意的是,timer 和 interval 均默认在新线程。
final SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.e(TAG,"开始时间是:" + dateformat.format(System.currentTimeMillis()));
Observable.timer(2, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
Log.e(TAG, "timer:" + aLong + "现在时间为:" + dateformat.format(System.currentTimeMillis()));
}
});
输出:
从日志可以看到,接收延迟了两秒
点击返回表格
internal
interval 操作符用于间隔时间执行某个操作,其接受三个参数,分别是第一次发送延迟,间隔时间,时间单位。
final SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.e(TAG,"开始时间是:" + dateformat.format(System.currentTimeMillis()));
Observable.interval(3, 2, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
Log.e(TAG, "internal:" + aLong + "at" + dateformat.format(System.currentTimeMillis()));
}
});
输出:
这个输出是一直在持续的,所以当我们的Activity 都销毁的时候,实际上这个操作还依然在进行,所以,我们得花点小心思让我们在不需要它的时候干掉它。查看源码发现,我们subscribe(Cousumer
Observable.just(1,2,3)
.doOnNext(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "先保存:" + integer);
}
})
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "subscribe开始打印:" + integer);
}
});
输出:
点击返回表格
skip
skip 作用和字面意思一样,接受一个 long 型参数 count ,代表跳过 count 个数目开始接收。
Observable.just(1,2,3,4,5,6,7)
.skip(2)
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "skip后的输出是:" + integer);
}
});
输出:
打印日志的结果很明显咯
点击返回表格
take
take,接受一个 long 型参数 count ,代表至多接收 count 个数据。
Observable.just(2,3,4,5)
.take(2)
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "take后的输出是:" + integer);
}
});
输出:
点击返回表格
just
just,没什么好说的,其实在前面各种例子都说明了,就是一个简单的发射器依次调用 onNext() 方法。
Observable.just(1,2,3)
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "just结果是:" + integer);
}
});
输出:
点击返回表格
Single
Single 只会接收一个参数,而 SingleObserver 只会调用 onError() 或者 onSuccess()
Single.just(new Random().nextInt())
.subscribe(new SingleObserver<Integer>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onSuccess(Integer integer) {
Log.e(TAG, "Success:" + integer);
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "Error:" + e.getMessage());
}
});
输出:
点击返回表格
debounce
过滤掉发送频率过快的事件
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
Thread.sleep(400);
emitter.onNext(2);
Thread.sleep(505);
emitter.onNext(3);
Thread.sleep(100);
emitter.onNext(4);
Thread.sleep(605);
emitter.onNext(5);
Thread.sleep(510);
emitter.onComplete();
}
}).debounce(500, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "debounce过滤后的输出结果是:" + integer);
}
});
输出:
这里过滤掉的是发射时间小于500ms的事件,所以1、3都没了
点击返回表格
defer
直到观察者订阅时才创建Observable,并且为每一个观察者创建一个新的Observable
Observable<Integer> observable = Observable.defer(new Callable<ObservableSource<? extends Integer>>() {
@Override
public ObservableSource<Integer> call() throws Exception {
return Observable.just(1, 2, 3);
}
});
observable.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Integer integer) {
Log.e(TAG, "defer : " + integer + "\n");
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "defer : onError : " + e.getMessage() + "\n");
}
@Override
public void onComplete() {
Log.e(TAG, "defer : onComplete\n");
}
});
输出:
点击返回表格
last
只能发射最后一个事件(或者满足某个条件所有项的最后一项)
在某些实现中,Last不是作为返回 Observable 的过滤运算符实现的,而是作为阻塞函数在源 Observable 终止时返回特定项目
Observable.just(1,5,9,20,15)
.last(11)
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "Last后的结果是: " + integer);
}
});
输出:
我在这里试过传入不同的整数参数,发现结果都是15
点击返回表格
merge
在Rx 操作符中,merge 的作用是把多个 Observable 结合起来,接受可变参数,也支持迭代器集合。注意它和 concat 的区别在于,不用等到 发射器 A 发送完所有的事件再进行发射器 B 的发送。
还有下图所示,来自任何源 Observable 的 onError 通知将立即传递给观察者,并终止合并的 Observable
在许多ReactiveX实现中,还有第二个操作符MergeDelayError改变了这种行为 - 保留onError通知,直到所有合并的Observables完成,然后才将其传递给观察者
Observable.merge(Observable.just(1,3,3),
Observable.just(4,5,6))
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "merge合并后的结果是:" + integer);
}
});
输出:
其实这里没有体现出merge的效用,如果需要同时发送多个网络请求可以试一下
点击返回表格
reduce
对Observable发出的每个项目按顺序去调用函数,然后发出最终值
Observable.just(1,2,3,4,5)
.reduce(new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) throws Exception {
return integer * integer2;
}
})
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "用reduce结合乘法后的结果是:" + integer);
}
});
输出:
点击返回表格
scan
scan 操作符作用和上面的 reduce 一致,唯一区别是 reduce 是个只追求结果,而 scan 还追求过程
Observable.just(1,2,3,4,5)
.scan(new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) throws Exception {
return integer * integer2;
}
})
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "scan结合乘法后的输出是:" + integer);
}
});
输出;
点击返回表格
window
定期将Observable中的项目细分为Observable窗口并发出这些窗口,而不是一次发送一个项目
window 与 buffer 相似,但不是从源 Observable 发送项目数据包,而是发射Observable ,每个Observable 从源 Observable 发射一个项目子集,然后通过onCompleted通知终止。
Observable.interval(1, TimeUnit.SECONDS)
.take(10)
.window(3, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Observable<Long>>() {
@Override
public void accept(Observable<Long> longObservable) throws Exception {
Log.e(TAG, "开始分窗 \n");
longObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
Log.e(TAG, "Next:" + aLong);
}
});
}
});
输出:
点击返回表格
未完待续
——————————————————