(6)RxJava2+Retrofit2+OkHttp3系列(RxJava2-6)

来源:互联网 发布:linux snmp起不来 编辑:程序博客网 时间:2024/05/22 07:00

上一节里我们学习了只使用Observable如何去解决上下游流速不均衡的问题,在这一节里我们先来学习如何使用Flowable。

正题

这里写图片描述

之前我们所的上游和下游分别是Observable和Observer, 这次不一样的是上游变成了Flowable, 下游变成了Subscriber, 但是水管之间的连接还是通过subscribe(), 我们来看看最基本的用法吧。

Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {            @Override            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {                Log.d(TAG, "emit 1");                emitter.onNext(1);                Log.d(TAG, "emit 2");                emitter.onNext(2);                Log.d(TAG, "emit 3");                emitter.onNext(3);                Log.d(TAG, "emit complete");                emitter.onComplete();            }        }, BackpressureStrategy.ERROR); //增加了一个参数        Subscriber<Integer> downstream = new Subscriber<Integer>() {            @Override            public void onSubscribe(Subscription s) {                Log.d(TAG, "onSubscribe");                s.request(Long.MAX_VALUE);  //注意这句代码            }            @Override            public void onNext(Integer integer) {                Log.d(TAG, "onNext: " + integer);            }            @Override            public void onError(Throwable t) {                 Log.w(TAG, "onError: ", t);            }            @Override            public void onComplete() {                Log.d(TAG, "onComplete");            }        };        upstream.subscribe(downstream);//结果onSubscribe   emit 1        onNext: 1     emit 2        onNext: 2     emit 3        onNext: 3     emit complete onComplete

我们注意到这次和Observable有些不同,首先是创建Flowable的时候增加了一个参数, 这个参数是用来选择背压,也就是出现上下游流速不均衡的时候应该怎么处理的办法, 这里我们直接用BackpressureStrategy.ERROR这种方式, 这种方式会在出现上下游流速不均衡的时候直接抛出一个异常,这个异常就是著名的MissingBackpressureException。

另外的一个区别是在下游的onSubscribe方法中传给我们的不再是Disposable了, 而是Subscription, 它俩有什么区别呢, 首先它们都是上下游中间的一个开关, 之前我们说调用Disposable.dispose()方法可以切断水管, 同样的调用Subscription.cancel()也可以切断水管, 不同的地方在于Subscription增加了一个void request(long n)方法, 这个方法有什么用呢?

我们将这句代码去除试试:

Flowable.create(new FlowableOnSubscribe<Integer>() {            @Override            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {                Log.d(TAG, "emit 1");                emitter.onNext(1);                Log.d(TAG, "emit 2");                emitter.onNext(2);                Log.d(TAG, "emit 3");                emitter.onNext(3);                Log.d(TAG, "emit complete");                emitter.onComplete();            }        }, BackpressureStrategy.ERROR).subscribe(new Subscriber<Integer>() {            @Override            public void onSubscribe(Subscription s) {                Log.d(TAG, "onSubscribe");            }            @Override            public void onNext(Integer integer) {                Log.d(TAG, "onNext: " + integer);            }            @Override            public void onError(Throwable t) {                Log.w(TAG, "onError: ", t);            }            @Override            public void onComplete() {                Log.d(TAG, "onComplete");            }        });

这次我们取消掉了request这句代码, 来看看运行结果:

zlc.season.rxjava2demo D/TAG: onSubscribezlc.season.rxjava2demo D/TAG: emit 1zlc.season.rxjava2demo W/TAG: onError:                               io.reactivex.exceptions.MissingBackpressureException: create: could not emit value due to lack of requests                                  at io.reactivex.internal.operators.flowable.FlowableCreate$ErrorAsyncEmitter.onOverflow(FlowableCreate.java:411)                                  at io.reactivex.internal.operators.flowable.FlowableCreate$NoOverflowBaseAsyncEmitter.onNext(FlowableCreate.java:377)                                  at zlc.season.rxjava2demo.demo.ChapterSeven$3.subscribe(ChapterSeven.java:77)                                  at io.reactivex.internal.operators.flowable.FlowableCreate.subscribeActual(FlowableCreate.java:72)                                  at io.reactivex.Flowable.subscribe(Flowable.java:12218)                                  at zlc.season.rxjava2demo.demo.ChapterSeven.demo2(ChapterSeven.java:111)                                  at zlc.season.rxjava2demo.MainActivity$2.onClick(MainActivity.java:36)                                  at android.view.View.performClick(View.java:5637)                                  at android.view.View$PerformClick.run(View.java:22429)                                  at android.os.Handler.handleCallback(Handler.java:751)                                  at android.os.Handler.dispatchMessage(Handler.java:95)                                  at android.os.Looper.loop(Looper.java:154)                                  at android.app.ActivityThread.main(ActivityThread.java:6119)                                  at java.lang.reflect.Method.invoke(Native Method)                                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)                                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)zlc.season.rxjava2demo D/TAG: emit 2zlc.season.rxjava2demo D/TAG: emit 3zlc.season.rxjava2demo D/TAG: emit complete

从运行结果中可以看到, 在上游发送第一个事件之后, 下游就抛出了一个著名的MissingBackpressureException异常, 并且下游没有收到任何其余的事件。可是这是一个同步的订阅呀, 上下游工作在同一个线程, 上游每发送一个事件应该会等待下游处理完了才会继续发事件啊, 不可能出现上下游流速不均衡的问题呀。

带着这个疑问, 我们再来看看异步的情况:

Flowable.create(new FlowableOnSubscribe<Integer>() {            @Override            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {                Log.d(TAG, "emit 1");                emitter.onNext(1);                Log.d(TAG, "emit 2");                emitter.onNext(2);                Log.d(TAG, "emit 3");                emitter.onNext(3);                Log.d(TAG, "emit complete");                emitter.onComplete();            }        }, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Subscriber<Integer>() {                    @Override                    public void onSubscribe(Subscription s) {                        Log.d(TAG, "onSubscribe");                        mSubscription = s;                    }                    @Override                    public void onNext(Integer integer) {                        Log.d(TAG, "onNext: " + integer);                    }                    @Override                    public void onError(Throwable t) {                        Log.w(TAG, "onError: ", t);                    }                    @Override                    public void onComplete() {                        Log.d(TAG, "onComplete");                    }                });//这次我们同样去掉了request这句代码, 但是让上下游工作在不同的线程onSubscribeemit 1emit 2emit 3emit complete

哎, 这次上游正确的发送了所有的事件, 但是下游一个事件也没有收到. 这是因为什么呢?

因为我们把request当做是一种能力, 当成下游处理事件的能力, 下游能处理几个就告诉上游我要几个, 这样只要上游根据下游的处理能力来决定发送多少事件, 就不会造成一窝蜂的发出一堆事件来, 从而导致OOM. 这也就完美的解决之前我们所学到的两种方式的缺陷, 过滤事件会导致事件丢失, 减速又可能导致性能损失. 而这种方式既解决了事件丢失的问题, 又解决了速度的问题。

学习了request, 我们就可以解释上面的两段同步和异步的代码了。

同步:为什么上游发送第一个事件后下游就抛出了MissingBackpressureException异常, 这是因为下游没有调用request, 上游就认为下游没有处理事件的能力, 而这又是一个同步的订阅, 既然下游处理不了, 那上游不可能一直等待吧, 如果是这样, 万一这两根水管工作在主线程里, 界面不就卡死了吗, 因此只能抛个异常来提醒我们。

异步:为什么上下游没有工作在同一个线程时, 上游却正确的发送了所有的事件呢? 这是因为在Flowable里默认有一个大小为128的水缸, 当上下游工作在不同的线程中时, 上游就会先把事件发送到这个水缸中, 因此, 下游虽然没有调用request, 但是上游在水缸中保存着这些事件, 只有当下游调用request时, 才从水缸里取出事件发给下游。

我们先来思考一下, 发送128个事件没有问题是因为FLowable内部有一个大小为128的水缸, 超过128就会装满溢出来。那既然你水缸这么小, 那我给你换一个大水缸如何:

Flowable.create(new FlowableOnSubscribe<Integer>() {            @Override            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {                for (int i = 0; ; i++) {                    emitter.onNext(i);                }            }        }, BackpressureStrategy.BUFFER).subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Subscriber<Integer>() {                    @Override                    public void onSubscribe(Subscription s) {                        Log.d(TAG, "onSubscribe");                        mSubscription = s;                    }                    @Override                    public void onNext(Integer integer) {                        Log.d(TAG, "onNext: " + integer);                    }                    @Override                    public void onError(Throwable t) {                        Log.w(TAG, "onError: ", t);                    }                    @Override                    public void onComplete() {                        Log.d(TAG, "onComplete");                    }                });

运行结果看图:

这里写图片描述

同样可以看到, 内存迅速增长, 直到最后抛出OOM。

我们在看看FLowable中剩下的两种策略:

  • BackpressureStrategy.DROP:Drop就是直接把存不下的事件丢弃
  • BackpressureStrategy.LATEST:,Latest就是只保留最新的事件

(1)我们先来看看BackpressureStrategy.DROP的实例。

public static void request() {        mSubscription.request(128);    }Flowable.create(new FlowableOnSubscribe<Integer>() {            @Override            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {                for (int i = 0; i < 10000; i++) {  //只发1w个事件                    emitter.onNext(i);                }            }        }, BackpressureStrategy.DROP).subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Subscriber<Integer>() {                    @Override                    public void onSubscribe(Subscription s) {                        Log.d(TAG, "onSubscribe");                        mSubscription = s;                        s.request(128);  //一开始就处理掉128个事件                    }                    @Override                    public void onNext(Integer integer) {                        Log.d(TAG, "onNext: " + integer);                    }                    @Override                    public void onError(Throwable t) {                        Log.w(TAG, "onError: ", t);                    }                    @Override                    public void onComplete() {                        Log.d(TAG, "onComplete");                    }                });

这里写图片描述

这段代码上游只发送了10000个事件,下游在一开始就立马处理掉了128个事件,然后我们在外部再调用request(128)试试,一开始下游就处理掉了128个事件, 当我们再次request的时候, 只得到了第3317的事件, 后面的事件直接被抛弃。

(2)我们再来看看BackpressureStrategy.LATEST的实例。

public static void request() {        mSubscription.request(128);    }public static void demo4() {        Flowable.create(new FlowableOnSubscribe<Integer>() {            @Override            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {                 for (int i = 0; i < 10000; i++) {  //只发1w个事件                    emitter.onNext(i);                }            }        }, BackpressureStrategy.LATEST).subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Subscriber<Integer>() {                    @Override                    public void onSubscribe(Subscription s) {                        Log.d(TAG, "onSubscribe");                        mSubscription = s;                        s.request(128);  //一开始就处理掉128个事件                    }                    @Override                    public void onNext(Integer integer) {                        Log.d(TAG, "onNext: " + integer);                    }                    @Override                    public void onError(Throwable t) {                        Log.w(TAG, "onError: ", t);                    }                    @Override                    public void onComplete() {                        Log.d(TAG, "onComplete");                    }                });    }

这里写图片描述

从运行结果中可以看到, 除去前面128个事件, 与Drop不同, Latest总是能获取到最后最新的事件, 例如这里我们总是能获得最后一个事件9999。

四种背压策略:

  • BackpressureStrategy.ERROR
  • BackpressureStrategy.BUFFER
  • BackpressureStrategy.DROP
  • BackpressureStrategy.LATEST
原创粉丝点击