RxJava学习笔记

https://www.jianshu.com/p/bb58571cdb64 该系列文章学习笔记,转化成自己的话,以后看起来比较容易懂。

引入:

implementation 'io.reactivex.rxjava2:rxjava:2.0.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
7753368-146603c7b74a81f1.png
image.png

上游发送事件,由下游接收,发送事件的是被观察者,而接收的是观察者,中间二者的连接就是subscribe() ,转化成代码就是

//被观察者 发送事件
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                Log.i("发送事件", "subscribe: 1");
                e.onNext("========11111");
                Log.i("发送事件", "subscribe: 2");
                e.onNext("========22222");
                Log.i("发送事件", "subscribe: 3");
                e.onNext("========33333");
                Log.i("发送事件", "subscribe: 4");
                e.onNext("========44444");
                e.onComplete();
            }
        }).subscribe(new Observer<String>() {
            //将上游下游连接起来  并且添加一个观察者,接收事件。

            @Override
            public void onSubscribe(Disposable d) {
                Log.d("订阅", "onSubscribe: ");
            }

            @Override
            public void onNext(String value) {
                Log.d("执行", "onNext: "+value);
            }

            @Override
            public void onError(Throwable e) {
                Log.d("错误", "onError: ");
            }

            @Override
            public void onComplete() {
                Log.d("完成", "onComplete: ");
            }
        });
    }

打印结果是:


7753368-0565f396ab97238d.png
image.png

可以看到的是:首先是订阅,只有订阅了之后才会开始发送事件,而且发送事件是以队列的形式发送的,发送一个执行一个。

ObservableEmitter和Disposable

ObservableEmitter: Emitter是发射器的意思,这个就是用来发出事件的,它可以发出三种类型的事件,通过调用emitter的onNext(T value)、onComplete()和onError(Throwable error)就可以分别发出next事件、complete事件和error事件。

但是,请注意,并不意味着你可以随意乱七八糟发射事件,需要满足一定的规则:

上游可以发送无限个onNext, 下游也可以接收无限个onNext.
当上游发送了一个onComplete后, 上游onComplete之后的事件将会继续发送, 而下游收到onComplete事件之后将不再继续接收事件.
当上游发送了一个onError后, 上游onError之后的事件将继续发送, 而下游收到onError事件之后将不再继续接收事件.
上游可以不发送onComplete或onError.
最为关键的是onComplete和onError必须唯一并且互斥, 即不能发多个onComplete, 也不能发多个onError, 也不能先发一个onComplete, 然后再发一个onError, 反之亦然

注: 关于onComplete和onError唯一并且互斥这一点, 是需要自行在代码中进行控制, 如果你的代码逻辑中违背了这个规则, **并不一定会导致程序崩溃. ** 比如发送多个onComplete是可以正常运行的, 依然是收到第一个onComplete就不再接收了, 但若是发送多个onError, 则收到第二个onError事件会导致程序会崩溃.

如果上游发送了一个onComplete(),效果是怎么样的呢?


7753368-e17652d4b29c87cd.png
image.png
 //被观察者 发送事件
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                Log.i("发送事件", "subscribe: 1");
                e.onNext("========11111");
                Log.i("发送事件", "subscribe: 2");
                e.onNext("========22222");
                Log.i("发送事件", "subscribe: 3");
                e.onNext("========33333");
                e.onComplete();//发送一个onComplete
                Log.i("发送事件", "subscribe: 4");
                e.onNext("========44444");
                e.onComplete();
            }
        }).subscribe(new Observer<String>() {
            //将上游下游连接起来  并且添加一个观察者,接收事件。

            @Override
            public void onSubscribe(Disposable d) {
                Log.d("订阅", "onSubscribe: ");
            }

            @Override
            public void onNext(String value) {
                Log.d("执行", "onNext: "+value);
            }

            @Override
            public void onError(Throwable e) {
                Log.d("错误", "onError: ");
            }

            @Override
            public void onComplete() {
                Log.d("完成", "onComplete: ");
            }
        });

打印结果:


7753368-72af277fe4444779.png
image.png

可以看到发送onComplete之后,只有发送事件,没有接收事件,因此印证了上面说的事件会继续发送但不会接收。发送onError也一样。


7753368-3a0bcd5663f706fd.png
image.png
Disposable

这个单词的字面意思是一次性用品,用完即可丢弃的. 那么在RxJava中怎么去理解它呢, 对应于上面的水管的例子, 我们可以把它理解成两根管道之间的一个机关, 当调用它的dispose()方法时, 它就会将两根管道切断, 从而导致下游收不到事件.

  //被观察者 发送事件
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                Log.i("发送事件", "subscribe: 1");
                e.onNext("========11111");
                Log.i("发送事件", "subscribe: 2");
                e.onNext("========22222");
                Log.i("发送事件", "subscribe: 3");
                e.onNext("========33333");
                Log.i("发送事件", "subscribe: 4");
                e.onNext("========44444");
                e.onComplete();
            }
        }).subscribe(new Observer<String>() {
            //将上游下游连接起来  并且添加一个观察者,接收事件。
            
            private Disposable disposable;
            private int I;

            @Override
            public void onSubscribe(Disposable d) {
                Log.d("订阅", "onSubscribe: ");
                disposable = d;
            }

            @Override
            public void onNext(String value) {
                Log.d("执行", "onNext: "+value);
                I++;
                if(i == 2){
                    disposable.dispose();
                    Log.i("是否切断", "onNext: "+disposable.isDisposed());
                }
            }

            @Override
            public void onError(Throwable e) {
                Log.d("错误", "onError: ");
            }

            @Override
            public void onComplete() {
                Log.d("完成", "onComplete: ");
            }
        });

如上代码所示,在i==2时,调用了disposable.dispose();切断了管道,那么下游就接收不到事件,打印出来如下:


7753368-20de71d957076b34.png
image.png

可以看到,事件一直在发送,但是下游并没有继续接收了。

另外, subscribe()有多个重载的方法:

    public final Disposable subscribe() {}
    public final Disposable subscribe(Consumer<? super T> onNext) {}
    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {} 
    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete) {}
    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe) {}
    public final void subscribe(Observer<? super T> observer) {}

不带任何参数的subscribe() :表示下游不关心任何事件,你上游尽管发你的数据去吧, 老子可不管你发什么.
带有一个Consumer参数的方法表示下游只关心onNext事件, 其他的事件我假装没看见, 因此我们如果只需要onNext事件可以这么写:

Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<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();
                Log.d(TAG, "emit 4");
                emitter.onNext(4);
            }
        }).subscribe(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) throws Exception {
                Log.d(TAG, "onNext: " + integer);
            }
        });
线程调度

关键方法:subscribeOn() 指定的是上游发送事件的线程, observeOn() 指定的是下游接收事件的线程.

多次指定上游的线程只有第一次指定的有效, 也就是说多次调用subscribeOn() 只有第一次的有效, 其余的会被忽略.
多次指定下游的线程是可以的, 也就是说每调用一次observeOn() , 下游的线程就会切换一次.

在RxJava中, 已经内置了很多线程选项供我们选择, 例如有

Schedulers.io() 代表io操作的线程, 通常用于网络,读写文件等io密集型的操作
Schedulers.computation() 代表CPU计算密集型的操作, 例如需要大量计算的操作
Schedulers.newThread() 代表一个常规的新线程
AndroidSchedulers.mainThread() 代表Android的主线程

Observable.create(new ObservableOnSubscribe<String>() {

            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                //上游发送事件
                Log.i("上游所在的线程", "subscribe: "+Thread.currentThread().getName());
                Log.i("上游发送事件", "subscribe: ");
                e.onNext("========1");

            }
        }).subscribeOn(Schedulers.newThread())//上游发送事件所在线程
                .observeOn(AndroidSchedulers.mainThread())//下游接收事件所在线程
                .subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                Log.i("下游所在的线程", "accept: "+Thread.currentThread().getName());
                Log.i("下游接收事件",s+"");
            }
        });

无论是做网络请求,还是做本地数据库的增删改查等,都是在上游,上游在子线程进行网络请求之后,发送消息给下游,下游在主线程接收消息,进行界面的更改等操作。

嵌套的网络请求

Map操作符 变化操作符,其作用就是对上游发送的每一个事件应用一个函数, 使得每一个事件都按照指定的函数去变化.


7753368-1e7606b2b265a56a.png
image.png

图中map中的函数作用是将圆形事件转换为矩形事件, 从而导致下游接收到的事件就变为了矩形.用代码来表示这个例子就是:

 Observable.create(new ObservableOnSubscribe<Integer>() {

            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                e.onNext(1);
            }
        }).map(new Function<Integer, String>() {

            @Override
            public String apply(Integer integer) throws Exception {
                Log.i("apply", "apply: "+integer);
                return integer+"";
            }
        }).subscribe(new Consumer<String>() {

            @Override
            public void accept(String s) throws Exception {
                Log.i("accept", "accept: "+s);
            }
        });

打印出的结果就是:


7753368-08e9716e7403824e.png
image.png

可以看到,下游接收的s就是apply()的返回值。也就是在apply中可以手动转化数据类型,再发送给下游。

flatMap
7753368-fd4eb066cd5c4184.png
image.png

上游每发送一个事件, flatMap都将创建一个新的水管, 然后发送转换之后的新的事件, 下游接收到的就是这些新的水管发送的数据. 这里需要注意的是, flatMap并不保证事件的顺序, 也就是图中所看到的, 并不是事件1就在事件2的前面. 如果需要保证顺序则需要使用concatMap.

zip关键字:
7753368-438f76dcc93958e4.png
图片.png

zip关键字是将上游两根管道里发送的数据结合起来,由下游接收。上游两根管道需要在两个不同的线程进行消息的发送。


7753368-3de354b5b89f2d70.png
图片.png

组合的过程是分别从 两根水管里各取出一个事件 来进行组合, 并且一个事件只能被使用一次, 组合的顺序是严格按照事件发送的顺利 来进行的, 也就是说不会出现圆形1 事件和三角形B 事件进行合并, 也不可能出现圆形2 和三角形A 进行合并的情况.
最终下游收到的事件数量 是和上游中发送事件最少的那一根水管的事件数量 相同. 这个也很好理解, 因为是从每一根水管 里取一个事件来进行合并, 最少的 那个肯定就最先取完 , 这个时候其他的水管尽管还有事件 , 但是已经没有足够的事件来组合了, 因此下游就不会收到剩余的事件了.

 Observable<String> observable1 = Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                e.onNext("1");
                e.onNext("2");
                e.onNext("3");
                e.onNext("4");
                e.onComplete();
            }
        }).subscribeOn(Schedulers.io());

        Observable<String> observable2 = Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(ObservableEmitter e) throws Exception {
                e.onNext("A");
                e.onNext("B");
                e.onNext("C");
                e.onComplete();
            }
        }).subscribeOn(Schedulers.io());


        Observable.zip(observable1, observable2, new BiFunction<String, String, String>() {
            @Override
            public String apply(String s, String s2) throws Exception {
                return s + s2;
            }
        }).subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {

                Log.i("订阅", "onSubscribe: ");

            }

            @Override
            public void onNext(String value) {

                Log.i("接收", "onNext: "+value);

            }

            @Override
            public void onError(Throwable e) {
                Log.i("错误", "onError: ");
            }

            @Override
            public void onComplete() {
                Log.i("完成", "onComplete: ");
            }
        });

打印出:
7753368-87897e7af211280e.png
图片.png

如果其中一个水管A发送事件特别快, 而另一个水管B 发送事件特别慢, 那就可能出现这种情况, 发得快的水管A 已经发送了1000个事件了, 而发的慢的水管B 才发一个出来, 组合了一个之后水管A 还剩999个事件, 这些事件需要继续等待水管B 发送事件出来组合, 那么这么多的事件是放在哪里的呢? 总有一个地方保存吧? 没错, Zip给我们的每一根水管都弄了一个水缸 , 用来保存这些事件, 用通俗易懂的图片来表示就是:


7753368-6a0ea9b66250eff2.png
图片.png

水缸并不是没有容量的,如果一直发送事件,会导致水缸爆掉,也就是造成OOM异常。那么怎么解决这个问题呢,第一、控制数量,第二、控制速度。

控制数量:

  1. 使用filter关键字去过滤掉一些数据,如:
 Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                for (int i = 0;;i++){
                    e.onNext(i+"");
                }
            }
        }).subscribeOn(Schedulers.io())
        .filter(new Predicate<String>() {
            @Override
            public boolean test(String s) throws Exception {
                return (s.equals("0") || s.equals("10"));
            }
        }).observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                Log.d("接收事件", "accept: "+s);
            }
        });

但是这种情况下,会丢失数据。

  1. 使用sample关键字,取样。
  Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                for (int i = 0;;i++){
                    e.onNext(i);
                }
            }
        }).subscribeOn(Schedulers.io())
        .sample(2, TimeUnit.SECONDS)//每隔两秒取一个事件放入水缸
                .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Consumer<Integer>() {
            @Override
            public void accept(Integer s) throws Exception {
                Log.d("接收事件", "accept: "+s);
            }
        });

减慢发送速度:

  Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                for (int i = 0;;i++){
                    e.onNext(i);
                    Thread.sleep(2000);//每次发送完睡两秒

                }
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Consumer<Integer>() {
            @Override
            public void accept(Integer s) throws Exception {
                Log.d("接收事件", "accept: "+s);
            }
        });
Flowable关键字:

使用flowable关键字时,需要添加两句代码,BackpressureStrategy.ERROR 和 s.request(Long.MAX_VALUE);第一句代码是添加背压,第二句代码是从上游的水缸中取出多少个交给下游。BackpressureStrategy.ERROR会在出现上下游流速不均衡的时候直接抛出一个异常,这个异常就是著名的MissingBackpressureException。request可以看成是下游处理事件的能力,上游根据下游能处理多少个事件来发送多少个事件。 如果下游没有调用request,那么上游会认为下游没有执行能力,会报MissingBackpressureException
在Flowable里默认有一个大小为128的水缸, 当上下游工作在不同的线程中时, 上游就会先把事件发送到这个水缸中, 因此, 下游虽然没有调用request, 但是上游在水缸中保存着这些事件, 只有当下游调用request时, 才从水缸里取出事件发给下游.
代码如下:

 Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> e) throws Exception {
                e.onNext(1);
                Log.i("发送", "onNext: "+1);
                e.onNext(2);
                Log.i("发送", "onNext: "+2);
                e.onNext(3);
                Log.i("发送", "onNext: "+3);
                e.onNext(4);
                Log.i("发送", "onNext: "+4);
                e.onComplete();
            }
        }, BackpressureStrategy.ERROR)
                 .subscribeOn(Schedulers.io())
                 .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.i("订阅", "onSubscribe: ");
//                        s.request(Long.MAX_VALUE);
                        s.request(2);
                    }

                    @Override
                    public void onNext(Integer integer) {
                        Log.i("接收", "onNext: "+integer);
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.i("错误", "onError: "+t);
                    }
                    @Override
                    public void onComplete() {
                        Log.i("完成", "onComplete: ");
                    }
                });

运行结果:


7753368-9081758e3dfa89d4.png
图片.png

我们看到上游发送了4个事件,但下游只接收了2个事件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值