- Rxjava2 基本用法(1)
- Rxjava2 操作符原理(2)
- Rxjava2 线程切换(3)
- Rxjava2 简析Flowable背压(4)
Rxjava官网中,给出了Rxjava的总结:
RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.
翻译过来:RxJava - JVM响应式扩展Reactive Extensions 用于使用Java VM的可观察序列编写异步和基于事件的程序的库。
这句话包含着两个关键词,异步的和基于事件的。
- 异步:非阻塞模式,且包含着观察者、被观察者和订阅关系,属于观察者设置模式。
- 基于事件:响应式编程,由Event触发。对事件的分发的库。
本文的编写和分析的版本如下:
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
一、简介
先从一个Demo引出:
// 1、观察者
final Observer observer = new Observer() {
@Override
public void onSubscribe(Disposable d) {
Log.e(TAG, "subscribe");
}
@Override
public void onNext(Integer value) {
Log.e(TAG, "" + value);
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "error");
}
@Override
public void onComplete() {
Log.e(TAG, "complete");
}
};
///2、被观察者
Observable observable = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
///4、发射相应事件
emitter.onNext(1);
emitter.onNext(2);
emitter.onComplete();
}
});
///3、订阅
observable.subscribe(observer);
- Observer 观察者
对于观察者而言,是对事件做出处理后的回调。Observer回调包含onSubscribe注册时回调,onNext事件到达时回调,onError抛出异常时回调,onComplete事件完成时回调。 - Observable 被观察对象
由该对象开始订阅,并对事件进行一系列的变换处理。最终该事件到达观察者的回调中。 - subscribe() 订阅
观察者订阅被观察者,订阅的时候会执行ObservableOnSubscribe中的subscribe订阅函数。 - ObservableEmitter 发射器
emitter.onNext(1);通过发射器发送一个事件,作为该事件的发起的源头。
Rxjava就如同工厂加工商品的流水线,事件就如同最初的商品,该商品可以进行加工(map等)将一个事件变成另外一个事件、过滤(filter)商品不合格过滤、组合(zip等)一个商品和另一个商品在一起加工成新的商品等等。
- onSubscribe 就像流水线开启电源,流水线被打开
- onNext 由最开始的材料,经过特殊的处理,生成了最终的商品
- onError 流水线发生了意外,如断电等,导致流水线不能正常运转,只能停止
- onComplete 流水线完成了所有的任务,比如计划生成100件商品,现在已经全部生产完毕,流水线就可以正常的断电了。
注意:
当调用了emitter.onComplete()、emitter.onError()回调后,剩下的事件就不会在发送了。可以按照流水线观念理解,工厂中的流水线都已经停电了,商品就不会再生产了,也就不会在发送事件了。
原因在于,发送之前会判断isDisposed()是否disposed,而onComplete和onError会设置该标志位。
二、Observable常见操作符
1)、创造型操作符
创造型操作符,就是用来创建一个Observable对象,是用来发送系列事件的源头。
①、create
用于创建一个带有发射器ObservableEmitter对象的Observable,可以用该发射器去发送一些事件。
代码如下,用create创建一个Observable,并使用ObservableEmitter发送了若干个事件。运行结果如上图。
Observable observable = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
///4、发射相应事件
emitter.onNext(1);
emitter.onNext(2);
emitter.onComplete();
}
});
②、fromArray
创建Observable对象,并将数组中的数据转换成一系列的事件发送。
函数的原型:
public static Observable fromArray(T... items)
Demo:分别发送1、2、3、4、5事件,发送完成会调用onComplete回调
Observable observable = Observable.fromArray(1,2,3,4,5);
③、just
同fromArray,底层也是调用fromArray函数实现的。
源码:从源码上看,就是调用fromArray函数。
@SuppressWarnings("unchecked")
@SchedulerSupport(SchedulerSupport.NONE)
public static Observable just(T item1, T item2, T item3) {
// 检测是否为空
ObjectHelper.requireNonNull(item1, "The first item is null");
ObjectHelper.requireNonNull(item2, "The second item is null");
ObjectHelper.requireNonNull(item3, "The third item is null");
// 通过fromArray实现
return fromArray(item1, item2, item3);
}
④、range
range的意思为范围,表示int型的范围。所range创建Observable对象,并将range范围中的数字当做事件发送。
Observable.range(1,5)
// 1、观察者
final Observer observer = new Observer() {
@Override
public void onSubscribe(Disposable d) {
Log.e(TAG, "subscribe");
}
@Override
public void onNext(Integer value) {
Log.e(TAG, "" + value);
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "error");
}
@Override
public void onComplete() {
Log.e(TAG, "complete");
}
};
///2、被观察者
Observable observable = Observable.range(1,5);
///3、订阅
observable.subscribe(observer);
结果:
subscribe
1
2
3
4
5
complete
方法原型:从start开始的连续count数
public static Observable range(final int start, final int count) {
⑤、interval
每间隔一段时间去发送Long型消息:
如:Observable
原型:period代表间隔时间,unit为时间单位。这样每个period时间就会发送一个从0开始的每次自增的Long型事件。
public static Observable interval(long period, TimeUnit unit)
⑥、timer
概念同interval类似,只不过interval会循环的发送,而timer只会推迟指定的事件发送。
如:Observable
原型:delay代表推迟时间,unit为时间单位。
public static Observable interval(long delay, TimeUnit unit)
2)、变换型操作符
变换性操作符,往往事件一种类型的事件转换成另外一种。如Integer转换成String。
①、map
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).map(new Function() {
@Override
public String apply(Integer integer) throws Exception {
return integer + "------";
}
}).subscribe(new Consumer() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, s);
}
});
结果:
1------
2------
3------
emitter发射器发送Integer型的事件,通过map操作符将integer型加个后缀变成String类型的。
- Function接口
map变换中有个Function,其中的apply函数,就是变换的关键点,通过调用重写的apply,就实现了事件的变换。
public interface Function {
R apply(T t) throws Exception;
}
②、flatMap
flatMap如同map一样,也是事件的变换。不同的在于apply函数,flatMap是转换成ObservableSource对象,用ObservableSource对数据进行了包装。这样就可以用ObservableSource二次处理了。
flatMap并不保证,事件发送的顺序,例如先发送的数据后回调。
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}).flatMap(new Function>() {
@Override
public ObservableSource apply(Integer integer) throws Exception {
if (integer == 2)
return Observable.just("I am value " + integer).delay(100, TimeUnit.MILLISECONDS);
else
return Observable.just("I am value " + integer).delay(10, TimeUnit.MILLISECONDS);
}
}).subscribe(new Consumer() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, s);
}
});
该功能是发送三个事件,对第一个和第三个事件推迟10毫秒再继续发送,对第二个推迟100毫米再继续分发下去。
从结果来看并没有保证顺序。结果:
I am value 1
I am value 3
I am value 2
小结flatMap变换成的对象是ObservableSource类型的,但是并不保证先后顺序。
③、concatMap
使用方法同flatMap完全一样,唯一不同点在于concatMap是绝对保证顺序,先发射的事件是先被回调的。
将上述代码中的flatMap改成concatMap后执行的结果就是:
I am value 1
I am value 2
I am value 3
④、buffer
他会截取count的事件,并把该事件组合成List
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
})
.buffer(5)
.subscribe(new Consumer>() {
@Override
public void accept(List list) throws Exception {
for (Integer integer : list) {
Log.e(TAG, "accept: " + integer);
}
}
});
3)、过滤型操作符
过滤型操作符,是过滤某些不合规则的事件。通过重写判断函数的标志来过滤的。
①、filter
通过判断发送的事件,从而过滤那些不符合规则的事件。
如:过滤不是偶数的事件
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
})
.filter(new Predicate() {
@Override
public boolean test(Integer integer) throws Exception {
return integer % 2 == 0;
}
})
.subscribe(new Consumer() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "accept: " + integer);
}
});
结果:
accept: 2
②、take(int count)
从第一个事件开始计算,只允许发送前面count个事件。
③、takeLast(int count)
只允许发送一系列事件中的最后count个事件。
④、distinct
distinct是过滤那些重复的事件。
如:只会显示不重复的事件
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(1);
emitter.onNext(1);
}
})
.distinct()
.subscribe(new Consumer() {
@Override
public void accept(Integer integer) throws Exception {
Log.e(TAG, "accept: " + integer);
}
});
4)、聚合操作符
用Observable发射的数据序列统计或者求值。
①、count
对传入的integer型数据求值。
如对1到10做加法运算,count后将求值后的结果继续传递。
Observable
.range(1,10)
.count()
.subscribe(new Consumer() {
@Override
public void accept(Long l) throws Exception {
Log.e(TAG, "accept: " + l);
}
});
②、concat
将两个同事件类型的Observable组合成一个Observable
Observable firstObservable = Observable.range(1,2);
Observable secondObservable = Observable.range(3,2);
Observable
.concat(firstObservable,secondObservable)
.subscribe(new Consumer() {
@Override
public void accept(Integer l) throws Exception {
Log.e(TAG, "accept: " + l);
}
});
结果:
accept: 1
accept: 2
accept: 3
accept: 4
③、toList
toList是将前面全部的事件或者count事件组合成为List
如:
Observable
.range(0,10)
.toList()
.subscribe(new Consumer>() {
@Override
public void accept(List list) throws Exception {
Log.e(TAG, "accept: " + list.size());
}
});
5)、聚合操作符
①、zip
使用一个指定的函数将多个Observable发射的数据组合在一起,然后将这个函数的结果作为单项数据发射。
每个Observable都会发送若干个事件,zip会分别获取每个Observable中的第n个事件,将每个Observable的第n个事件一起传递给用户,再根据情况处理。
但每个Observable发送的长度不一样,那就按照木桶效应来处理,一律按照最短的截取。
如:
Observable firstObservable = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
});
Observable secondObservable = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext("-----1");
emitter.onNext("-----2");
emitter.onNext("-----3");
emitter.onNext("------");
}
});
Observable resultObservable = firstObservable.zipWith(secondObservable, new BiFunction() {
@Override
public String apply(Integer integer, String string) throws Exception {
return integer + string;
}
});
resultObservable.subscribe(new Consumer() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, "accept: " + s);
}
});
结果:
accept: 1-----1
accept: 2-----2
accept: 3-----3
分别将同一级的integer型、string型的事件一同交给用户同意处理。第一个Observable发送了三个事件,第二个Observable发送了四个事件,那就按照最小的来,只处理三个。
三、背压问题
1)、原因
一项技术或功能的产生,必定是由于出现了某个特定的问题,或者原来的技术已经不太满足现在的需求了。
背压问题,用三峡大坝比喻下。大坝的用处就是分为拦截上流流入的水,通过大坝释放到下游,如果上游的水流入的比释放的快,就可能出现问题。但也不一样,毕竟三峡的水位警戒线比较高,一般的流速肯定不在话下。最怕出现了那种暴雨天气,已经逼近了警戒线,大坝全力释洪,还不行该怎么办的问题。
其实这就是生产者和消费者问题,用上述的水比作事件,这就出现了生产和消费的不平衡现象。也就是背压问题。
如:ObservableEmitter不断的发送事件,另外一个线程处理事件。这就导致了处理速度比不上发送速度。
Observable
.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
for (int i = 0; ; ) {
emitter.onNext(i++);
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(Integer integer) throws Exception {
}
});
这样就最终会导致OOM内存溢出。
先看几个关键类:
- Flowable
Flowable用法和Observable类似,在这基础上多了上游和下游处理速度不平衡的交互功能,该功能需要Subscription类配合才能完成。 - Subscriber
注册的回调,使用Observable时注册的回调是Observer对象,而对于Flowable类则是Subscriber对象。
Observer和Subscriber不同点在于,这两个类的注册成功回调对象不一致。Observer对应着Disposable,而Flowable对应着Subscriber。Subscriber多了和上游交互的功能。
- Subscription
该对象会在注册成功后的注册回调中返回。
public interface Subscription {
public void request(long n);
public void cancel();
}
cancel()会切断整个流水线,达到关闭作用。重要的在于request,这会告诉发射器发射器可以发射几个事件。
下面看下例子:
Flowable flowable = Flowable.create(new FlowableOnSubscribe() {
@Override
public void subscribe(FlowableEmitter emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
}
}, BackpressureStrategy.ERROR);
Subscriber subscriber = new Subscriber() {
@Override
public void onSubscribe(Subscription s) {
Log.e(TAG, "onSubscribe");
// 告诉上游可以处理3个事件
s.request(3);
}
@Override
public void onNext(Integer integer) {
Log.e(TAG, "onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Log.e(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.e(TAG, "onComplete");
}
};
flowable.subscribe(subscriber);
简单的用法,通过Subscription.request()来告诉上游可以处理多少个事件,但是这并不影响上游的发送速度,上游还是像傻瓜样,该怎么发这么发。最终会使缓冲池装满。
如果上述去除s.request(3);就会抛出异常,该异常和设置的BackpressureStrategy.ERROR策略有关。如果发送的数据超过了请求的大小,就会抛出如下的错误。
onError:
io.reactivex.exceptions.MissingBackpressureException: create: could not emit value due to lack of requests
正确的使用就是和上游通过emitter.requested()来感知下游能够处理多少。如果==0就表示下游不在接收了,那我就不发了。
Flowable flowable = Flowable.create(new FlowableOnSubscribe() {
@Override
public void subscribe(FlowableEmitter emitter) throws Exception {
for (int i=0;;) {
while (!emitter.isCancelled() && emitter.requested() == 0) {
Thread.sleep(10);
}
if (emitter.isCancelled())
break;
emitter.onNext(i++);
}
}
}, BackpressureStrategy.ERROR);
Subscriber subscriber = new Subscriber() {
Subscription mSubscription = null;
@Override
public void onSubscribe(Subscription s) {
Log.e(TAG, "onSubscribe");
mSubscription = s;
// 开始请求一个触发一次
s.request(1);
}
@Override
public void onNext(Integer integer) {
Log.e(TAG, "onNext: " + integer);
mSubscription.request(1);
}
@Override
public void onError(Throwable t) {
Log.e(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.e(TAG, "onComplete");
}
};
flowable.subscribe(subscriber);
在上述的例子中,1、FlowableEmitter中只有获得的下游的request数大于0时才继续的发送事件。2、观察者收到了注册的方法中,主动的s.request(1);让发射器发一个数据。3、观察者在onNext中接收到事件后,再继续的s.request(1)请求相应的事件。
策略:
BackpressureStrategy中有相应的策略。它的枚举定义如下:
public enum BackpressureStrategy {
/**
* OnNext events are written without any buffering or dropping.
* Downstream has to deal with any overflow.
* Useful when one applies one of the custom-parameter onBackpressureXXX operators.
*/
MISSING,
/**
* Signals a MissingBackpressureException in case the downstream can't keep up.
*/
ERROR,
/**
* Buffers all onNext values until the downstream consumes it.
*/
BUFFER,
/**
* Drops the most recent onNext value if the downstream can't keep up.
*/
DROP,
/**
* Keeps only the latest onNext value, overwriting any previous value if the
* downstream can't keep up.
*/
LATEST
}
- BUFFER 当发送的事件有来不及处理的时候,会放在缓冲区里面,这个缓冲区会无限的增加,直到发生OOM
- ERROR 当FlowableEmitter发射器在emitter.requested() == 0的时候发送就会抛出异常
- DROP Rxjava默认的缓冲区为128,如果有来不及处理的事件,就会放到缓冲区,128个放满后,接下来的事件就会抛弃。
- LATEST 与DROP策略类似,他会抛弃最开始的数据,缓冲最后的数据。
以上的分析都是在同一个线程中。现在看下不同线程的情况。
例如:FlowableEmitter发送128个事件,Subscription请求一个事件。
Flowable flowable = Flowable.create(new FlowableOnSubscribe() {
@Override
public void subscribe(FlowableEmitter emitter) throws Exception {
Log.e(TAG, "subscribe: " + emitter.requested());
for (int i = 0; i < 128; i++) {
Log.e(TAG, "subscribe: "+i );
emitter.onNext(i);
}
}
}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
Subscriber subscriber = new Subscriber() {
Subscription mSubscription = null;
@Override
public void onSubscribe(Subscription s) {
Log.e(TAG, "onSubscribe");
mSubscription = s;
// 负责请求一个事件
s.request(1);
}
@Override
public void onNext(Integer integer) {
Log.e(TAG, "onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Log.e(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.e(TAG, "onComplete");
}
};
flowable.subscribe(subscriber);
结果:
subscribe: 128
subscribe: 0
subscribe: 1
subscribe: 2
.........
subscribe: 126
subscribe: 127
onNext:0
生产者和消费者在异步的情况下,它的事件容量池就默认是128个。这样就可以默认发送128个。但是却不能消费128个事件,只能消费1个事件。
原因是发送是在一个线程中,回调是在另一个线程。每个线程都会保存request,两个地方的request对象是不一样的。
目前有两个问题:
- 1、明明回调中设置了s.request(1);,但是subscribe()打印的是128,并不是1,和之前同步的是不一样的
- 2、既然subscribe()中是128,那为什么下游只能处理1个事件
subscribe()打印的是128,只有等最开始的128个事件,消费了96个,容量池才会重新的设置成回调中设置的1.
subscribe()中128,代表发射器可以发送128个事件,低于128是不会抛出异常的。而只能消费1个代表着消费者s.request(1)就请求消费一个。
四、线程变换
对于前面操作符介绍章节,全部都是在主线程中操作的。也就是说事件的处理和事件的回调全在同一个线程中。一般的模式,对于耗时的操作放在子线程中,最后再将处理的结果切换到主线程,进行结果的回调。
对于一个事件流,它的线程变换决定是变换该事件点之前的线程,或者变换该点之后的线程。
- subscribeOn(Scheduler scheduler)
改变该点之前的线程,可以设置多次,但只能是第一个才有用 - observeOn(Scheduler scheduler)
改变该点之后的线程,可以设置多次,但只有最后一次才有用
例子
Observable
.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext(1);
Log.e(TAG, "subscribe: " + Thread.currentThread().getName());
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new Function() {
@Override
public String apply(Integer integer) throws Exception {
Log.e(TAG, "apply: " + Thread.currentThread().getName());
return String.valueOf(integer);
}
})
.observeOn(Schedulers.io())
.subscribe(new Consumer() {
@Override
public void accept(String s) throws Exception {
Log.e(TAG, "accept: " + Thread.currentThread().getName());
}
});
结果:
subscribe: main
apply: main
accept: RxCachedThreadScheduler-3
虽然subscribeOn设置了多次,但只会对第一次设置的有用。observeOn决定之后在哪个线程,但两个同时设置的observeOn,最后一个才会生效,它的作用范围是在两个observeOn之间。
Schedulers类型
- AndroidSchedulers.mainThread() : 主线程
- Schedulers.io() : io操作的线程, 通常用于网络,读写文件等io密集型的操作
- Schedulers.computation() : CPU计算密集型的操作
- Schedulers.newThread() : 新建一个线程
五、引用
- https://www.jianshu.com/p/f54f32b39b7c
- https://www.jianshu.com/p/36e0f7f43a51
- http://gank.io/post/560e15be2dca930e00da1083