1 背压
在RxJava中,会遇到被观察者发送消息太快以至于它的操作符或者订阅者不能及时处理相关的消息,这就是典型的背压(Back Pressure)场景。
BackPressure经常被翻译为背压,背压的字面意思比较晦涩,难以理解。它是指在异步场景下,被观察者发送事件速度远快于观察者处理的速度,从而导致下游的buffer溢出,这种现象叫做背压。
产生条件:
- 异步,被观察者和观察者处于不同的线程中。
- 被观察者发送消息的速度远远快于观察者处理的的数据
在RxJava2.x中,只有Flowable类型是支持背压的,并且Flowable很多操作符内部都使用了背压策略,从而避免过多的数据填满内部的队列。
解决背压问题的方法:
1.1 过滤限流
通过使用限流操作符将被观察者产生的大部分事件过滤并抛弃,以达到限流的目的,间接降低事件发射的速度,例如使用以下操作符:
- sample:在一段时间内,只处理最后一个数据。
- throttleFirst:在一段时间内,只处理第一个数据。
- debounce:发送一个数据,开始计时,到了规定时间,若没有再发送数据,则开始处理数据,反之重新开始计时。
1.2 打包缓存
在被观察者发射事件过快导致观察者来不及处理的情况下,可以使用缓存类操作符将其中一部分打包缓存起来,再一点一点的处理其中的事件。
- buffer:将多个事件打包放入一个List中,再一起发射。
- window:将多个事件打包放入一个Observable中,再一起发射。
1.3 使用背压操作符
通过一些操作符来转化成支持背压的Observable
2. RxJava2.x的背压策略
在RxJava2.x中,Observable不在支持背压,而是改用Flowable来专门支持背压。默认队列大小为128,并且要求所有的操作符强制支持背压。
Flowable一共有5中背压策略(BackpressureStategy中定义):MISSING,ERROR,BUFFER,DROP,LATEST
2.1 MISSING
通过Create方法创建Flowable没有指定背压策略,不会通过onNext发射的数据做缓存或丢弃处理,需要下游通过飞呀操作符(onBackpressureBuffer()/onBackpressureDrop()/onBackpressureLatest())指定背压策略。
Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(FlowableEmitter<Integer> emitter) throws Exception {for (int i = 0; i <1000 ; i++) {emitter.onNext(i);}}},BackpressureStrategy.MISSING).onBackpressureBuffer().subscribe(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) throws Exception {System.out.println(integer);}});
上面的代码执行的是buffer的背压策略。
2.2 ERROR
如果放入Flowable的异步缓存池中的数据超限了,则会抛出MissingBackpressureException异常
Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(FlowableEmitter<Integer> emitter) throws Exception {for (int i = 0; i < 129; i++) {emitter.onNext(i);}}}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) throws Exception {System.out.println(integer);}});
在Android中运行以上代码,会立即引起App Crash,引起以下Exception:
W/System.err: io.reactivex.exceptions.MissingBackpressureException: create: could not emit value due to lack of requests
W/System.err: at io.reactivex.internal.operators.flowable.FlowableCreate$ErrorAsyncEmitter.onOverflow(FlowableCreate.java:438)at io.reactivex.internal.operators.flowable.FlowableCreate$NoOverflowBaseAsyncEmitter.onNext(FlowableCreate.java:406)
因为Flowable的默认队列是128,所以讲上述代码的129改成128,程序就可以正常运行了。
2.3 BUFFER
Flowable的异步缓存池同Observable的一样,没有固定大小,可以无限制添加数据,不会抛出MissingBackpressureException异常,但会导致OOM(Out of Memory).
Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(FlowableEmitter<Integer> emitter) throws Exception {for (int i = 0;; i++) {emitter.onNext(i);}}}, BackpressureStrategy.BUFFER).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) throws Exception {System.out.println(integer);}});
上述代码不会导致崩溃,但会引起ANR。
2.4 DROR
如果Flowable的异步缓存池满了,则会丢掉将要放入缓存池中的数据。
Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(FlowableEmitter<Integer> emitter) throws Exception {for (int i = 0;i<129; i++) {emitter.onNext(i);}}}, BackpressureStrategy.DROP).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) throws Exception {System.out.println(integer);}});
在Android中运行这段代码,不会引起Crash,但只会打印出0~127,第128则被丢弃,因为Flowable的内部队列已经满了。
2.5 LATEST
如果缓存池满了,会丢掉将要放入缓存池中的数据。这一点与DROP策略一样,不同的是,不管缓存池的状态如何,LATEST策略会将最后一条数据强行放入缓存池中。
Flowable.create(new FlowableOnSubscribe<Integer>() {@Overridepublic void subscribe(FlowableEmitter<Integer> emitter) throws Exception {for (int i = 0;i<1000; i++) {emitter.onNext(i);}}}, BackpressureStrategy.LATEST).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) throws Exception {System.out.println(integer);}});
执行结果:
0
1
...
127
516
519
521
523
526
528
530
...
734
737
740
743
745
748
999
3. 其他
Flowable不仅可以通过create创建时需要制定背压策略,还可以在通过其他创建操作符,例如just、fromArray等创建后通过背压操作符指定背压策略。例如,
onBackpressreBuffer()对应BackpressureStategy.BUFFER,
onBackpressreDrop()对应BackpressureStategy.DROP,
onBackpressreLatest()对应BackpressureStategy.LATEST.
示例:
Flowable.interval(1, TimeUnit.MILLISECONDS).onBackpressureBuffer().subscribe(new Consumer<Long>() {@Overridepublic void accept(Long aLong) throws Exception {System.out.println(aLong);}});