概念
我们知道上游发布事件,下游处理事件,有没有想过,如果上游发布事件很快,而下游处理事件很慢会发生什么情况,看下面代码:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
for (int i = 0;; i++) {
System.out.println("emit i " + i);
e.onNext(i);
}
}
}).subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer s) throws Exception {
Thread.sleep(500);
System.out.println("onNext " + s);
}
});
System.out: emit i 0
System.out: onNext 0
System.out: emit i 1
System.out: onNext 1
System.out: emit i 2
System.out: onNext 2
···
上游是个死循环,不断发数据,下游处理数据时睡眠了500毫秒,但是从输出结果发现上游来一个,下游便处理一个,上游与下游同步执行。其实这样的结果可想而知是因为上游和下游工作在同一线程,同步执行,下游睡眠结束后,上游才再次发送数据。好,我们现在把他们放到不同线程执行下试试:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
for (int i = 0; ; i++) {
e.onNext(i);
}
}
}).observeOn(Schedulers.io()).subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer s) throws Exception {
Thread.sleep(500);
System.out.println("onNext " + s);
}
});
下图是运行这段代码,通过 android studio 的 profiler 看到的实时内存大小:
可以看到,当上游与下游不在同一线程,异步执行时,如果上游发送事件很快,下游不能及时处理时,就会导致应用占用内存非常大,甚至OOM。
既然问题是因为上游发送事件大于下游处理事件,那我们是否可以控制上游发送的速度来解决的? 有几个操作符确实是可以控制, 上篇博文中提到的sample,throttleFirst,debounce 操作符都可以,但是它们都有个问题,它们并不是真正去控制上游的速度,只是丢弃了很多事件,只接受时间范围内的某一个事件。另外还有一个 buffer 操作符 ,它可以先将上游的事件放到缓冲池里面,等达到指定数量后才发给下游。
Observable.range(1, 1000)
.subscribeOn(Schedulers.io())
.buffer(10)
.observeOn(Schedulers.io())
.subscribe(System.out::println);
System.out: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
System.out: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
System.out: [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
...
那有没有真正可以控制上游的速度,响应式拉取事件,下游能处理多少事件就发送多少事件,或者说下游处理一个,上游发送一个,的方法呢?
那就是 背压 。
背压就是解决异步环境下,上游与下游效率不一样时的方法策略。
Observable 是不支持背压的,而后来的 Flowable 支持,这是 Observable 和 Flowable 主要的区别。
下一篇博文中将介绍 Flowable 。