RxJava2.0教程(五)

RxJava2.0教程(五)

先来回顾一下上上节,我们讲Flowable的时候,说它采用了响应式拉的方式,我们还举了个叶问打小日本的例子,再来回顾一下吧,我们说把上游看成小日本, 把下游当作叶问, 当调用Subscription.request(1)时, 叶问就说我要打一个! 然后小日本就拿出一个鬼子给叶问, 让他打, 等叶问打死这个鬼子之后, 再次调用request(10), 叶问就又说我要打十个! 然后小日本又派出十个鬼子给叶问, 然后就在边上看热闹, 看叶问能不能打死十个鬼子, 等叶问打死十个鬼子后再继续要鬼子接着打。

但是不知道大家有没有发现,在我们前两节中的例子中,我们口中声称的响应式拉并没有完全体现出来,比如这个例子:

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;  
                        s.request(1);
                    }

                    @Override
                    public void onNext(Integer integer) {
                        Log.d(TAG, "onNext: " + integer);
                        mSubscription.request(1);
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete");
                    }
                });

虽然我们在下游中是每次处理掉了一个事件之后才调用request(1)去请求下一个事件,也就是说叶问的确是在打死了一个鬼子之后才继续打下一个鬼子,可是上游呢?上游真的是每次当下游请求一个才拿出一个吗?从上上篇文章中我们知道并不是这样的,上游仍然是一开始就发送了所有的事件,也就是说小日本并没有等叶问打死一个才拿出一个,而是一开始就拿出了所有的鬼子,这些鬼子从一开始就在这儿排队等着被打死。

有个故事是这么说的:

楚人有卖盾与矛者,先誉其盾之坚,曰:“吾盾之坚,物莫能陷也。”俄而又誉其矛之利,曰:“吾矛之利,万物莫不陷也。”市人诘之曰:"以子之矛陷子之盾,何如?”其人弗能应也。众皆笑之。

没错,我们前后所说的就是自相矛盾了,这说明了什么呢,说明我们的实现并不是一个完整的实现,那么,究竟怎样的实现才是完整的呢?

我们先自己来想一想,在下游中调用Subscription.request(n)就可以告诉上游,下游能够处理多少个事件,那么上游要根据下游的处理能力正确的去发送事件,那么上游是不是应该知道下游的处理能力是多少啊,对吧,不然,一个巴掌拍不响啊,这种事情得你情我愿才行。

那么上游从哪里得知下游的处理能力呢?我们来看看上游最重要的部分,肯定就是FlowableEmitter了啊,我们就是通过它来发送事件的啊,来看看它的源码吧(别紧张,它的代码灰常简单):

public interface FlowableEmitter<T> extends Emitter<T> {
    void setDisposable(Disposable s);
    void setCancellable(Cancellable c);

    /**
     * The current outstanding request amount.
     * <p>This method is thread-safe.
     * @return the current outstanding request amount
     */
    long requested();

    boolean isCancelled();
    FlowableEmitter<T> serialize();
}

FlowableEmitter是个接口,继承Emitter,Emitter里面就是我们的onNext(),onComplete()和onError()三个方法。我们看到FlowableEmitter中有这么一个方法:

long requested();

方法注释的意思就是当前外部请求的数量,哇哦,这好像就是我们要找的答案呢. 我们还是实际验证一下吧.

先来看同步的情况吧:

 public static void demo1() {
        Flowable
                .create(new FlowableOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                        Log.d(TAG, "current requested: " + emitter.requested());
                    }
                }, BackpressureStrategy.ERROR)
                .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数量,下游什么也不做。

我们先猜测一下结果,下游没有调用request(),说明当前下游的处理能力为0,那么上游得到的requested也应该是0,是不是呢?

来看看运行结果:

D/TAG: onSubscribe
D/TAG: current requested: 0

哈哈,结果果然是0,说明我们的结论基本上是对的。

那下游要是调用了request()呢,来看看:

 public static void demo1() {
        Flowable
                .create(new FlowableOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                        Log.d(TAG, "current requested: " + emitter.requested());
                    }
                }, BackpressureStrategy.ERROR)
                .subscribe(new Subscriber<Integer>() {

                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                        s.request(10); //我要打十个!
                    }

                    @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(10),告诉上游我要打十个,看看运行结果:

D/TAG: onSubscribe
D/TAG: current requested: 10

果然!上游的requested的确是根据下游的请求来决定的,那要是下游多次请求呢?比如这样:

public static void demo1() {
        Flowable
                .create(new FlowableOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                        Log.d(TAG, "current requested: " + emitter.requested());
                    }
                }, BackpressureStrategy.ERROR)
                .subscribe(new Subscriber<Integer>() {

                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                        s.request(10);  //我要打十个!
                        s.request(100); //再给我一百个!
                    }

                    @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(10), 然后又调用了request(100),来看看运行结果:

D/TAG: onSubscribe
D/TAG: current requested: 110

看来多次调用也没问题,做了加法

诶加法?对哦,只是做加法,那什么时候做减法呢?

当然是发送事件啦!

来看个例子吧:

 public static void demo2() {
        Flowable
                .create(new FlowableOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(final FlowableEmitter<Integer> emitter) throws Exception {
                        Log.d(TAG, "before emit, requested = " + emitter.requested());

                        Log.d(TAG, "emit 1");
                        emitter.onNext(1);
                        Log.d(TAG, "after emit 1, requested = " + emitter.requested());

                        Log.d(TAG, "emit 2");
                        emitter.onNext(2);
                        Log.d(TAG, "after emit 2, requested = " + emitter.requested());

                        Log.d(TAG, "emit 3");
                        emitter.onNext(3);
                        Log.d(TAG, "after emit 3, requested = " + emitter.requested());

                        Log.d(TAG, "emit complete");
                        emitter.onComplete();

                        Log.d(TAG, "after emit complete, requested = " + emitter.requested());
                    }
                }, BackpressureStrategy.ERROR)
                .subscribe(new Subscriber<Integer>() {

                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                        s.request(10);  //request 10
                    }

                    @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");
                    }
                });
    }

代码很简单,来看看运行结果:

D/TAG: onSubscribe                        
D/TAG: before emit, requested = 10        
D/TAG: emit 1                             
D/TAG: onNext: 1                          
D/TAG: after emit 1, requested = 9        
D/TAG: emit 2                             
D/TAG: onNext: 2                          
D/TAG: after emit 2, requested = 8        
D/TAG: emit 3                             
D/TAG: onNext: 3                          
D/TAG: after emit 3, requested = 7        
D/TAG: emit complete                      
D/TAG: onComplete                         
D/TAG: after emit complete, requested = 7 

大家应该能看出端倪了吧,下游调用request(n) 告诉上游它的处理能力,上游每发送一个next事件之后,requested就减一,注意是next事件,complete和error事件不会消耗requested,当减到0时,则代表下游没有处理能力了,这个时候你如果继续发送事件,会发生什么后果呢?当然是MissingBackpressureException啦,试一试:

public static void demo2() {
        Flowable
                .create(new FlowableOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(final FlowableEmitter<Integer> emitter) throws Exception {
                        Log.d(TAG, "before emit, requested = " + emitter.requested());

                        Log.d(TAG, "emit 1");
                        emitter.onNext(1);
                        Log.d(TAG, "after emit 1, requested = " + emitter.requested());

                        Log.d(TAG, "emit 2");
                        emitter.onNext(2);
                        Log.d(TAG, "after emit 2, requested = " + emitter.requested());

                        Log.d(TAG, "emit 3");
                        emitter.onNext(3);
                        Log.d(TAG, "after emit 3, requested = " + emitter.requested());

                        Log.d(TAG, "emit complete");
                        emitter.onComplete();

                        Log.d(TAG, "after emit complete, requested = " + emitter.requested());
                    }
                }, BackpressureStrategy.ERROR)
                .subscribe(new Subscriber<Integer>() {

                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                        s.request(2);   //request 2
                    }

                    @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(2), 看看运行结果:

 D/TAG: onSubscribe
 D/TAG: before emit, requested = 2
 D/TAG: emit 1
 D/TAG: onNext: 1
 D/TAG: after emit 1, requested = 1
 D/TAG: emit 2
 D/TAG: onNext: 2
 D/TAG: after emit 2, requested = 0
 D/TAG: emit 3
 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.ChapterNine$4.subscribe(ChapterNine.java:80)
                 at io.reactivex.internal.operators.flowable.FlowableCreate.subscribeActual(FlowableCreate.java:72)
                 at io.reactivex.Flowable.subscribe(Flowable.java:12218)
                 at zlc.season.rxjava2demo.demo.ChapterNine.demo2(ChapterNine.java:89)
                 at zlc.season.rxjava2demo.MainActivity$2.onClick(MainActivity.java:36)
                 at android.view.View.performClick(View.java:4780)
                 at android.view.View$PerformClick.run(View.java:19866)
                 at android.os.Handler.handleCallback(Handler.java:739)
                 at android.os.Handler.dispatchMessage(Handler.java:95)
                 at android.os.Looper.loop(Looper.java:135)
                 at android.app.ActivityThread.main(ActivityThread.java:5254)
                 at java.lang.reflect.Method.invoke(Native Method)
                 at java.lang.reflect.Method.invoke(Method.java:372)
                 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
                 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
 D/TAG: after emit 3, requested = 0
 D/TAG: emit complete
 D/TAG: after emit complete, requested = 0

到目前为止我们一直在说同步的订阅,现在同步说完了,我们先用一张图来总结一下同步的情况:

同步request.png

这张图的意思就是当上下游在同一个线程中的时候,在下游调用request(n)就会直接改变上游中的requested的值,多次调用便会叠加这个值,而上游每发送一个事件之后便会去减少这个值,当这个值减少至0的时候,继续发送事件便会抛异常了。

我们再来说说异步的情况,异步和同步会有区别吗?会有什么区别呢?带着这个疑问我们继续来探究。

同样的先来看一个基本的例子:

public static void demo3() {
        Flowable
                .create(new FlowableOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                        Log.d(TAG, "current requested: " + emitter.requested());
                    }
                }, 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");
                    }
                });
    }

这次是异步的情况,上游啥也不做,下游也啥也不做,来看看运行结果:

D/TAG: onSubscribe
D/TAG: current requested: 128

哈哈,又是128,看了我前几篇文章的朋友肯定很熟悉这个数字啊!这个数字为什么和我们之前所说的默认的水缸大小一样啊,莫非?

带着这个疑问我们继续来研究一下:

 public static void demo3() {
        Flowable
                .create(new FlowableOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                        Log.d(TAG, "current requested: " + emitter.requested());
                    }
                }, BackpressureStrategy.ERROR)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {

                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                        s.request(1000); //我要打1000个!!
                    }

                    @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(1000)告诉上游我要打1000个,按照之前我们说的,这次的运行结果应该是1000,来看看运行结果:

D/TAG: onSubscribe
D/TAG: current requested: 128

卧槽,你确定你没贴错代码?

是的,真相就是这样,就是128,蜜汁128。。。

what happened?
I don't know !

为了答疑解惑,我就直接上图了:

异步request.png

可以看到,当上下游工作在不同的线程里时,每一个线程里都有一个requested,而我们调用request(1000)时,实际上改变的是下游主线程中的requested,而上游中的requested的值是由RxJava内部调用request(n)去设置的,这个调用会在合适的时候自动触发。

现在我们就能理解为什么没有调用request,上游中的值是128了,因为下游在一开始就在内部调用了request(128)去设置了上游中的值,因此即使下游没有调用request(),上游也能发送128个事件,这也可以解释之前我们为什么说Flowable中默认的水缸大小是128,其实就是这里设置的。

刚才同步的时候我们说了,上游每发送一个事件,requested的值便会减一,对于异步来说同样如此,那有人肯定有疑问了,一开始上游的requested的值是128,那这128个事件发送完了不就不能继续发送了吗?

刚刚说了,设置上游requested的值的这个内部调用会在合适的时候自动触发,那到底什么时候是合适的时候呢?是发完128个事件才去调用吗?还是发送了一半才去调用呢?

带着这个疑问我们来看下一段代码:

 public static void request() {
        mSubscription.request(96); //请求96个事件
    }

public static void demo4() {
        Flowable
                .create(new FlowableOnSubscribe<Integer>() {
                    @Override
                    public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                        Log.d(TAG, "First requested = " + emitter.requested());
                        boolean flag;
                        for (int i = 0; ; i++) {
                            flag = false;
                            while (emitter.requested() == 0) {
                                if (!flag) {
                                    Log.d(TAG, "Oh no! I can't emit value!");
                                    flag = true;
                                }
                            }
                            emitter.onNext(i);
                            Log.d(TAG, "emit " + i + " , requested = " + emitter.requested());
                        }
                    }
                }, 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");
                    }
                });
    }

这次的上游稍微复杂了一点点,首先仍然是个无限循环发事件,但是是有条件的,只有当上游的requested != 0的时候才会发事件,然后我们调用request(96)去消费96个事件(为什么是96而不是其他的数字先不要管),来看看运行结果吧:

D/TAG: onSubscribe
D/TAG: First requested = 128
D/TAG: emit 0 , requested = 127
D/TAG: emit 1 , requested = 126
D/TAG: emit 2 , requested = 125
  ...
D/TAG: emit 124 , requested = 3
D/TAG: emit 125 , requested = 2
D/TAG: emit 126 , requested = 1
D/TAG: emit 127 , requested = 0
D/TAG: Oh no! I can't emit value!

首先运行之后上游便会发送完128个事件,之后便不做任何事情,从打印的结果中我们也可以看出这一点。

然后我们调用request(96),这会让下游去消费96个事件,来看看运行结果吧:

D/TAG: onNext: 0
D/TAG: onNext: 1
  ...
D/TAG: onNext: 92
D/TAG: onNext: 93
D/TAG: onNext: 94
D/TAG: onNext: 95
D/TAG: emit 128 , requested = 95
D/TAG: emit 129 , requested = 94
D/TAG: emit 130 , requested = 93
D/TAG: emit 131 , requested = 92
  ...
D/TAG: emit 219 , requested = 4
D/TAG: emit 220 , requested = 3
D/TAG: emit 221 , requested = 2
D/TAG: emit 222 , requested = 1
D/TAG: emit 223 , requested = 0
D/TAG: Oh no! I can't emit value!

可以看到,当下游消费掉第96个事件之后,上游又开始发事件了,而且可以看到当前上游的requested的值是96(打印出来的95是已经发送了一个事件减一之后的值),最终发出了第223个事件之后又进入了等待区,而223-127 正好等于 96。

这是不是说明当下游每消费96个事件便会自动触发内部的request()去设置上游的requested的值啊!没错,就是这样,而这个新的值就是96。

朋友们可以手动试试请求95个事件,上游是不会继续发送事件的。

至于这个96是怎么得出来的(肯定不是我猜的蒙的啊),感兴趣的朋友可以自行阅读源码寻找答案,对于初学者而言应该没什么必要,管它内部怎么实现的呢对吧。

好了今天的教程就到这里了!通过本节的学习,大家应该知道如何正确的去实现一个完整的响应式拉取了,在某一些场景下,可以在发送事件前先判断当前的requested的值是否大于0,若等于0则说明下游处理不过来了,则需要等待,例如下面这个例子。

实践

这个例子是读取一个文本文件,需要一行一行读取,然后处理并输出,如果文本文件很大的时候,比如几十M的时候,全部先读入内存肯定不是明智的做法,因此我们可以一边读取一边处理,实现的代码如下:

 public static void main(String[] args) {
        practice1();
        try {
            Thread.sleep(10000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void practice1() {
        Flowable
                .create(new FlowableOnSubscribe<String>() {
                    @Override
                    public void subscribe(FlowableEmitter<String> emitter) throws Exception {
                        try {
                            FileReader reader = new FileReader("test.txt");
                            BufferedReader br = new BufferedReader(reader);

                            String str;

                            while ((str = br.readLine()) != null && !emitter.isCancelled()) {
                                while (emitter.requested() == 0) {
                                    if (emitter.isCancelled()) {
                                        break;
                                    }
                                }
                                emitter.onNext(str);
                            }

                            br.close();
                            reader.close();

                            emitter.onComplete();
                        } catch (Exception e) {
                            emitter.onError(e);
                        }
                    }
                }, BackpressureStrategy.ERROR)
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.newThread())
                .subscribe(new Subscriber<String>() {

                    @Override
                    public void onSubscribe(Subscription s) {
                        mSubscription = s;
                        s.request(1);
                    }

                    @Override
                    public void onNext(String string) {
                        System.out.println(string);
                        try {
                            Thread.sleep(2000);
                            mSubscription.request(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onError(Throwable t) {
                        System.out.println(t);
                    }

                    @Override
                    public void onComplete() {
                    }
                });
    }

运行的结果便是:




在很久以前的一篇文章中,提到过如何利用Retrofit中的GsonConverter来处理API请求错误的方法,地址在这儿,今天给大家介绍另外一种优雅的方法,利用RxJava内部的RxJavaPlugins来做这么一个骚操作。

正题

说到RxJavaPlugins可能有很多朋友还很陌生,毕竟我们日常开放也不会怎么接触这个东西,但是从它的名字上来看就应该觉得它不一般,毕竟人家名字里带了一个Plugin,废话少说,我们先来看一下这个类到底是什么东西。

先找到这个类的位置,在io.reactivex.plugins这个包中,这个包就这一个类,再来看看类的定义:

package io.reactivex.plugins;
...
/**
 * Utility class to inject handlers to certain standard RxJava operations.
 */
public final class RxJavaPlugins {
    ....
}

首先映入眼帘的就是这句类注释了,来翻译一下:用于将一些骚操作注入到某些标准RxJava操作的工具类。

听上去好像很牛逼啊!我们来看一下它里面到底写了些什么骚操作:

//代码太长了,随便粘贴几句
public final class RxJavaPlugins {
    static volatile Consumer<? super Throwable> errorHandler;
    static volatile Function<? super Runnable, ? extends Runnable> onScheduleHandler;
    static volatile Function<? super Callable<Scheduler>, ? extends Scheduler> onInitComputationHandler;
    ...
    static volatile Function<? super Scheduler, ? extends Scheduler> onComputationHandler;
    static volatile Function<? super Scheduler, ? extends Scheduler> onSingleHandler;
    static volatile Function<? super Scheduler, ? extends Scheduler> onIoHandler;
    ...
    static volatile BiFunction<? super Flowable, ? super Subscriber, ? extends Subscriber> onFlowableSubscribe;
    static volatile BiFunction<? super Maybe, ? super MaybeObserver, ? extends MaybeObserver> onMaybeSubscribe;
    static volatile BiFunction<? super Observable, ? super Observer, ? extends Observer> onObservableSubscribe;
    ...
    public static Consumer<? super Throwable> getErrorHandler() {}
    public static void setErrorHandler(@Nullable Consumer<? super Throwable> handler) {}
    ...
}

看到这里,我相信大家应该都是和我一样的想法:这他吗是啥啊。。。为什么每个字母我都认识,写到一起我就不知道什么意思了。。。

懵逼

先不慌。。我们先粗略看一下这个类的结构,emmmm…先是定义了一大堆static的变量,但是没有public出来,所以应该会有对应的getter和setter方法,好像就是这样,没毛病,好了,到此为止,这个类可以关了,也看不出啥东西来。。

既然这条路碰壁了,那我们换一条路来试试。

先来看一段正常得不能再正常的RxJava代码:

        Maybe.just(1)
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        Log.d(TAG, "Real onSuccess");
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.d(TAG, "Real onError");
                    }
                });

运行的结果就是:

zlc.season.javademo D/MainActivity: Real onSuccess

看过之前的教程的都知道这个subscribe()方法是个很重要的方法啦,那我们就来看看这个方法到底干了啥!

之前说过,subscribe方法有多个重载的方法,通过源码得知,这些重载的方法最后都会调用到其中的一个subscribe方法中:

public final void subscribe(MaybeObserver<? super T> observer) {
        ObjectHelper.requireNonNull(observer, "observer is null");

        observer = RxJavaPlugins.onSubscribe(this, observer);

        ObjectHelper.requireNonNull(observer, "observer returned by the RxJavaPlugins hook is null");

        try {
            subscribeActual(observer);
        } catch (NullPointerException ex) {...
        } catch (Throwable ex) {...}
    }

通过这个源码我们一下子就找到了一行关键的代码:

observer = RxJavaPlugins.onSubscribe(this, observer);

先简单解释一下,这里的this就是当前的Maybe对象,也就是我们的上游,这里的observer就是我们的下游。

这意味着什么呢,意味着RxJavaPlugins对我们的subscribe方法做了一个骚操作呀!

这样我们一下子就找到了RxJavaPlugins和调用链之间的联系,接下来就需要顺藤摸瓜,更加深入的了解一下,来看一下RxJavaPlugins.onSubscribe()的源码吧:

//为了便于理解,把源码中的范型去掉了
public final class RxJavaPlugins {
    ...
    static volatile BiFunction onMaybeSubscribe;
    ...
    public static void setOnMaybeSubscribe(BiFunction onMaybeSubscribe) {
        RxJavaPlugins.onMaybeSubscribe = onMaybeSubscribe;
    }
    ...
    //source就是我们的上游,observer就是我们的下游
    public static  MaybeObserver onSubscribe(Maybe source, MaybeObserver observer) {
        BiFunction f = onMaybeSubscribe;   
        if (f != null) {     //如果onMaybeSubscribe不为空
            return apply(f, source, observer); //调用apply方法创建一个新的下游
        }
        return observer;    
    }
    ...
    static MaybeObserver apply(BiFunction f, Maybe source, MaybeObserver observer) {
        return f.apply(source, observer);  
    }
}

这个代码简直不能再清晰了,大概就是如果我调用了setOnMaybeSubscribe()设置了一个BiFunction类型的变量onMaybeSubscribe,那么当我调用subscribe()方法的时候就会调用这个变量的apply()方法来做一个骚操作返回一个新的下游,否则就原封不动的把原来的下游返回。

这就给了我们无限的想象力啊,我们可以通过这个apply()方法直接把原本的下游返回,这样就什么也不做,也可以包装一下原来的下游,在真正的下游的方法执行前后插入一些自己的操作,哇哦,好像很厉害的样子。。。

那既然要包装,首先肯定得有一个包装类:

class WrapDownStreamObserver<T> implements MaybeObserver<T> {

        private MaybeObserver<T> actual;

        public WrapDownStreamObserver(MaybeObserver<T> actual) {
            this.actual = actual;
        }

        @Override
        public void onSubscribe(Disposable d) {
            actual.onSubscribe(d);
        }

        @Override
        public void onSuccess(T t) {
            Log.d(TAG, "Hooked onSuccess");
            actual.onSuccess(t);
        }

        @Override
        public void onError(Throwable e) {
            Log.d(TAG, "Hooked onError");
            actual.onError(e);
        }

        @Override
        public void onComplete() {
            Log.d(TAG, "Hooked onComplete");
            actual.onComplete();
        }
    }

这就是一个简单的包装类了,它和下游都是同样的类型,并且内部持有真正的下游,我们在真正的下游方法调用前都插入了一条日志。

有了包装类,那么我们就可以调用RxJavaPlugins的setOnMaybeSubscribe()方法来做骚操作了:

RxJavaPlugins.setOnMaybeSubscribe(new BiFunction<Maybe, MaybeObserver, MaybeObserver>() {
            @Override
            public MaybeObserver apply(Maybe maybe, MaybeObserver maybeObserver) throws Exception {
                return new WrapDownStreamObserver(maybeObserver); //这个maybeObserver就是我们真正的下游
            }
        });

接下来就是拭目以待的运行结果啦:

zlc.season.javademo D/MainActivity: Hooked onSuccess
zlc.season.javademo D/MainActivity: Real onSuccess

哈哈,果不其然,不愧是骚操作!!果然在真正的下游执行前先去执行了包装类里的代码,似乎已经看见了胜利的曙光!!

不过刚才的是在同一个线程的代码,我们再来一个带有线程切换的代码验证一下:

        Maybe.just(1)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        Log.d(TAG, "Real onSuccess");
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.d(TAG, "Real onError");
                    }
                });

当我们满怀信心的时候,生活总是会给你泼一盆冷水:

zlc.season.javademo D/MainActivity: Hooked onSuccess
zlc.season.javademo D/MainActivity: Hooked onSuccess
zlc.season.javademo D/MainActivity: Hooked onSuccess
zlc.season.javademo D/MainActivity: Real onSuccess

发生了什么?是不是代码贴错了啊?为什么会打印三次Hooked onSuccess。。。我明明只包装了一个下游呀。。。

这个问题要详细的解释清楚估计得花一段时间了,这里就直接给出答案了,因为我们使用RxJavaPluginssetOnMaybeSubscribe()方法实际上是给所有的Maybe类型的subscribe()方法都做了一个骚操作,而在我们的RxJava调用链中,除了我们的上游下游,其实还有中游,这些中游位于RxJava的内部,我们每做一次链式调用,都会生成一个新的中游,因此我们的骚操作不仅仅只对下游生效,对这些中游也会生效,所以出现上面的打印结果。从代码也可以看出来,我们分别调用了一次subscribeOn和一次observeOn,因此对应的产生了两个中游,再加上我们自己的下游,所以一共打印三次Hooked onSuccess也说得通。

但是尽管打印了这么多,我们还是可以从中看到,我们的骚操作依然是有效的,在真正的下游方法执行前,依然执行了包装类中的代码,所以我们的这个方案是完全可行的,只需要避免一下重复处理就可以了。

看到这里,广大吃瓜群众估计还是处于一脸懵逼的状态。。。这TM跟我处理API错误有啥关系?

铲你一耳屎.jpg

emmmm...目前来说好像确实没什么太大的关系。。。但是,下面这段代码看完你也许就明白了。

我们继续来看一段Retrofit请求的代码:

public interface Api {
    @GET
    Maybe<BaseResponse> getSomeThing(@Url String url); //注意这里使用的是Maybe
}

private void requestSomeThing(String url) {
        api.getSomeThing(url)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<BaseResponse>() {
                    @Override
                    public void accept(BaseResponse baseResponse) throws Exception {
                        if(baseResponse.getCode()==100){
                            //Token 过期,跳转登录页面。。。
                            ....
                        }else if(...){
                            ...
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, "Something wrong", throwable);
                        if (throwable instanceof ConnectionException) {
                            Log.d(TAG, "没有网络连接");
                        } else if (throwable instanceof SocketTimeoutException) {
                            Log.d(TAG, "连接超时");
                        } else {
                            //...
                        }
                    }
                });
    }

这是一段普通的请求代码,包含了请求成功了要判断code是否正确,判断token是否过期,请求失败了要针对不同的异常情况来做不同的处理,等一系列操作。

通过前面的铺垫,我相信大家心里都有点B number了,我们只需要把判断code是否正确,token是否过期,以及异常的情况放到包装类里,这样不就做到统一处理了吗?

先别急,可能细心一点的朋友就发现了,我们这里Api 接口定义的时候使用的是Maybe,而我们知道,在RxJava2中除了Maybe,还有SingleCompletableObservableFlowable,我们定义接口也可以写成:

public interface Api {
    @GET  //Maybe
    Maybe<BaseResponse> getSomeThing(@Url String url); 
   
    @GET   //Observable
    Observable<BaseResponse> getSomeThing(@Url String url); 
    
    @GET   //Flowable
    Flowable<BaseResponse> getSomeThing(@Url String url); 
    ...
}

那是不是意味着我们要对每一个都要用RxJavaPlugin来做骚操作啊?

答案是不需要,我们只需要对Observable做骚操作就行了!是的,就是Observable,为什么只需要对Observable做骚操作呢?这个答案可以从RetrofitRxJava2CallAdapter中找到答案:

final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {
    ......
        
    @Override
    public Object adapt(Call<R> call) {
        //这就是我们真正的上游
        Observable<Response<R>> responseObservable = isAsync
                ? new CallEnqueueObservable<>(call)
                : new CallExecuteObservable<>(call);

        Observable<?> observable;
        if (isResult) {
            observable = new ResultObservable<>(responseObservable);
        } else if (isBody) {
            observable = new BodyObservable<>(responseObservable);
        } else {
            observable = responseObservable;
        }

        if (scheduler != null) {
            observable = observable.subscribeOn(scheduler);
        }

        if (isFlowable) {
            return observable.toFlowable(BackpressureStrategy.LATEST);
        }
        if (isSingle) {
            return observable.singleOrError();
        }
        if (isMaybe) {
            return observable.singleElement();
        }
        if (isCompletable) {
            return observable.ignoreElements();
        }
        return observable;
    }
}

从这个代码中可以看到,我们请求真正的上游其实是一个Observable,我们在Api接口中定义的不管是Maybe,还是Flowable,其实都是在Observable做了一次链式调用而已,所以我们只需要对Observable做一个骚操作,就可以了。

所以我们先来创建一个Observer的包装类:

class ObservableSubscribeHooker<T> implements Observer<T> {
        private Observer<T> actual;

        public ObservableSubscribeHooker(Observer<T> actual) {
            this.actual = actual;
        }
    
        @Override
        public void onSubscribe(Disposable d) {
            actual.onSubscribe(d);
        }

        @Override
        public void onNext(T t) {
            hookOnNext(t);
            actual.onNext(t);
        }

        private void hookOnNext(T t) {
            if (t instanceof BaseResponse) {
                BaseResponse baseResponse = (BaseResponse) t;
                if (baseResponse.getCode() == 100) {
                    //登录过期,跳转到登录页
                    ...
                    throw new Exceptions.TokenExpired(); //注意这里的trick
                }
            }
        }

        @Override
        public void onError(Throwable e) {

            if (e instanceof ConnectException) {
                Log.e(TAG, "Connect failed: ", e);
                //处理ConnectException
                ...
                actual.onError(new Exceptions.Offline()); //注意这里的trick
                return;
            }

            if (e instanceof SocketTimeoutException) {
                Log.e(TAG, "Time out ", e);
                //处理SocketTimeoutException
                ...
                actual.onError(new Exceptions.TimeOut()); //注意这里的trick
                return;
            }

            //其余的异常处理...

            actual.onError(e);
        }

        @Override
        public void onComplete() {
            actual.onComplete();
        }
    }

注意这里面的几个小Trick,通过自定义的异常,避免了重复处理的问题,并且下游仍然可以针对自己的特殊情况进行自己的特殊处理。

接下来就是设置到RxJavaPlugins中了:

public class CustomApplication extends Application {
  
    @Override
    public void onCreate() {
        super.onCreate();
       
        RxJavaPlugins.setOnObservableSubscribe(new BiFunction<Observable, Observer, Observer>() {
            @Override
            public Observer apply(Observable observable, Observer observer) throws Exception {
                return new ObservableSubscribeHooker(observer);
            }
        });

    }
}

好啦,今天的教程就写到这里吧~

最终的demo已经上传到GitHub,地址在 链接在这里


猜你喜欢

转载自blog.csdn.net/qq_22714623/article/details/80998239
今日推荐