rxjava2中的背压Backpressure策略:
在rxjava常见的使用场景我们一般的操作步骤如下:
1.创建被观察者Observable对象在上游生产事件(Event)
2.通过一系列的操作符operators以及subscribeOn()来对上游Observable生产的数据进行加工处理以及
Observable 生产数据的线程的调度控制
3.通过创建下游的观察者Observer以及observeOn()来控制Observer对上游发送数据的处理以及下游Observer在
哪个线程里面进行数据的处理
4.最后通过sunscribe()来使 Observable和Observer之间产生订阅关系
由于在rxjava中引入了线程控制因此我们就会有以下的两种情形发生:
1.被观察者Observable发射事件和 观察者Observer订阅响应事件在同一个线程中:
此时上游每发射一条数据下游就会处理一次上游发射的数据依次往复的进行而不会造成上下游流速不匹配的问题而导致
的事件丢失 甚至内存溢出oom的发生
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
for (int j = 0; j < 5; j++) {
SystemClock.sleep(500); //上游的Observable每隔0.5s发射一条数据
Log.d(TAG, "subscribe: "+j + " thread : " + Thread.currentThread().getName());
e.onNext(j);
}
}
}).subscribeOn(Schedulers.trampoline())
.observeOn(Schedulers.trampoline())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
SystemClock.sleep(3000); //下游的Observer每隔3s才响应一次上游Observable发射的数据
Log.d(TAG, "accept: " + integer + " thread : " + Thread.currentThread().getName());
}
});
打印结果:
2.被观察者Observable和观察者Observer发射响应数据不在同一个线程上
上游发射一个数据而下游则是每隔3s才接受响应一个数据由此就造成了一个上下游流速不匹配的问题
由于上游发射的数据过快而下游没有及时响应导致了上下游的流速不匹配,此时rxjava会将上游发射而下游没有来的及
处理的事件数据给缓存起来而不是给丢弃掉或者被垃圾回收器给回收,因此当缓存的事件越来越多就会造成缓存区溢出
导致事件丢失 甚至导致内存溢出oom的产生
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
for(int i = 0;;i++){
Log.d(TAG, "subscribe: "+i + " thread : " + Thread.currentThread().getName());
}
}
}).subscribeOn(Schedulers.newThread()) //Observable生产发射数据在单独的新的线程中
.observeOn(Schedulers.newThread()) //Observer响应订阅数据也在一个单独的线程
.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
SystemClock.sleep(3000);
Log.d(TAG, "accept: " + integer + " thread : " + Thread.currentThread().getName());
}
});
观察结果:
为了解决以上问题rxjava中就引入了backpressurestrategy背压策略:
背压(BackPressure)的含义:上游生产速度大于下游处理速度,导致下游处理不及时的情况,被称为BackPressure
<- 来自工程的概念back pressure(背压)
更通俗的解释是:对于可丢弃的事件,上游生产过快导致事件堆积,当堆积到超出buffer缓冲区上限,就叫做Backpressure出现
背压一词在rxjava中听起来很常见,但其实在安卓中用到的地方并不是很多
在安卓中适合支持Backpressure的情况有以下几种:
1.在线直播视频流
我们把视频流看成一个连续不断的事件序列,当我们的手机出现了卡顿的时候视频界面会产生丢帧的现象
此时我们就可以使用backpressure背压来处理,此时我们可以直接将由于卡顿前面没有处理的视频帧给丢弃掉
2.Log
3.用户数请求数超限(服务端)
因此对于背压我们主要要记住两点:
1.事件的产生是不可控的
2.产生的事件是可丢弃的
在背压策略中我们发射和接收数据的对象由之前的Observable和Observer,变成了Flowable和Subscriber两个角色
但是Flowable和Subscriber还是通过subscribe()来产生订阅的关系
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> e) throws Exception {
for (int i = 0; i < 3; i++) {
Log.d(TAG, "subscribe: 发送事件" + i + " thread : " + Thread.currentThread().getName());
e.onNext(i);
}
Log.d(TAG, "subscribe: 发送完成 thread : " + Thread.currentThread().getName());
e.onComplete();
}
}, BackpressureStrategy.BUFFER) //使用Flowable发射数据需要指定一种背压策略
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.newThread())
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) { //onSubscribe()方法中由Observable的Disposable变成了现在的subscription了
s.request(Long.MAX_VALUE); //此行代码必须加上否则下游的subscriber无法接收上游Flowable发送的事件
Log.d(TAG, "onSubscribe: 开始subscribe连接 thread : " + Thread.currentThread().getName());
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext: " + integer + " thread : " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable t) {
Log.d(TAG, "onError: " + " thread : " + Thread.currentThread().getName());
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete: " + " thread : " + Thread.currentThread().getName());
}
});
打印结果:
在rxjava1.0中实现背压是使用Observable和observer
而在rxjava2.0中实现背压则是使用Flowable和Subscriber来实现的
Rxjava2.0中Flowable在背压策略上的使用的思路主要如下:
1.控制观察者Subscriber接收事件的速度,通过Subscription的request(n)方法:
//在rxjava2.0中引入了Flowable来代替1.0版本时候的Observable来重新实现背压的策略
//通过Subscription的request(n)方法来控制观察者Subscriber接收事件的速度
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> e) throws Exception {
Log.d(TAG, "发送了事件 : 1" + Thread.currentThread().getName());
e.onNext(1);
Log.d(TAG, "发送了事件 : 2" + Thread.currentThread().getName());
e.onNext(2);
Log.d(TAG, "发送了事件 : 3" + Thread.currentThread().getName());
e.onNext(3);
Log.d(TAG, "发送了事件 : 4" + Thread.currentThread().getName());
e.onNext(4);
Log.d(TAG, "发送了事件 : complete" + Thread.currentThread().getName());
e.onComplete();
}
},BackpressureStrategy.ERROR)
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.newThread())
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
//通过Subscription的request方法来控制观察者Subscriber能够接收到几个上游Flowable发送的事件
//request(n)方法控制接受事件的个数主要是针对上游发送的next事件而言的(complete error事件不算入其中)
s.request(3); //虽然上游发送了4个next事件,但是观察者Subscriber通过Subscription.request(3)只接收3个事件
Log.d(TAG, "onSubscribe: 开始subscribe连接 thread : " + Thread.currentThread().getName());
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext: " + integer + " thread : " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable t) {
Log.d(TAG, "onError: " + " thread : " + Thread.currentThread().getName());
t.printStackTrace();
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete: " + " thread : &#