Carson带你学Android:RxJava过滤操作符

前言

Rxjava,由于其基于事件流的链式调用、逻辑简洁 & 使用简单的特点,深受各大 Android开发者的欢迎。

如果还不了解 RxJava,请看文章:Android:这是一篇 清晰 & 易懂的Rxjava 入门教程

  • RxJava如此受欢迎的原因,在于其提供了丰富 & 功能强大的操作符,几乎能完成所有的功能需求
  • 今天,我将为大家详细介绍RxJava操作符中最常用的 过滤操作符,希望你们会喜欢。

Carson带你学RxJava系列文章,包括 原理、操作符、应用场景、背压等等,请关注看文章:Android:这是一份全面 & 详细的RxJava学习指南


目录

示意图

1. 作用

过滤 / 筛选 被观察者(Observable)发送的事件 & 观察者 (Observer)接收的事件


2. 类型

  • RxJava2中,过滤操作符的类型包括:

    示意图

  • 下面,我将对每个操作符进行详细讲解

3. 应用场景 & 对应操作符详解

  • 过滤操作符的应用场景包括:
    1. 根据 指定条件 过滤事件
    2. 根据 指定事件数量 过滤事件
    3. 根据 指定时间 过滤事件
    4. 根据 指定事件位置 过滤事件
  • 下面,我将根据上述应用场景,讲解对应的操作符使用

注:在使用RxJava 2操作符前,记得在项目的Gradle中添加依赖:

dependencies {
      compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
      compile 'io.reactivex.rxjava2:rxjava:2.0.7'
      // 注:RxJava2 与 RxJava1 不能共存,即依赖不能同时存在
}

3.1 根据 指定条件 过滤事件

  • 需求场景
    通过设置指定的过滤条件,当且仅当该事件满足条件,就将该事件过滤(不发送)

  • 对应操作符类型

示意图
  • 对应操作符使用

Filter()

  • 作用
    过滤 特定条件的事件

  • 原理

示意图
  • 具体使用
Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                // 1. 发送5个事件
                emitter.onNext(1);
                emitter.onNext(2);
                emitter.onNext(3);
                emitter.onNext(4);
                emitter.onNext(5);
            }

            // 2. 采用filter()变换操作符
        }).filter(new Predicate<Integer>() {
            // 根据test()的返回值 对被观察者发送的事件进行过滤 & 筛选
              // a. 返回true,则继续发送
              // b. 返回false,则不发送(即过滤)
            @Override
            public boolean test(Integer integer) throws Exception {
                return integer > 3;
                // 本例子 = 过滤了整数≤3的事件
            }
        }).subscribe(new Observer<Integer>() {

            @Override
            public void onSubscribe(Disposable d) {
                Log.d(TAG, "开始采用subscribe连接");
            }

            @Override
            public void onNext(Integer value) {
                Log.d(TAG, "过滤后得到的事件是:"+ value  );
            }

            @Override
            public void onError(Throwable e) {
                Log.d(TAG, "对Error事件作出响应");
            }

            @Override
            public void onComplete() {
                Log.d(TAG, "对Complete事件作出响应");
            }
        });
  • 测试结果
示意图

ofType()

  • 作用
    过滤 特定数据类型的数据

  • 具体使用

Observable.just(1, "Carson", 3, "Ho", 5)
                  .ofType(Integer.class) // 筛选出 整型数据
                  .subscribe(new Consumer<Integer>() {
                      @Override
                      public void accept( Integer integer) throws Exception {
                          Log.d(TAG,"获取到的整型事件元素是: "+ integer);
                      }
        });
  • 测试结果
示意图

skip() / skipLast()

  • 作用
    跳过某个事件

  • 具体使用

// 使用1:根据顺序跳过数据项
        Observable.just(1, 2, 3, 4, 5)
                .skip(1) // 跳过正序的前1项
                .skipLast(2) // 跳过正序的后2项
                  .subscribe(new Consumer<Integer>() {
                      @Override
                      public void accept( Integer integer) throws Exception {
                          Log.d(TAG,"获取到的整型事件元素是: "+ integer);
                      }
        });

// 使用2:根据时间跳过数据项
        // 发送事件特点:发送数据0-5,每隔1s发送一次,每次递增1;第1次发送延迟0s
        Observable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS)
                .skip(1, TimeUnit.SECONDS) // 跳过第1s发送的数据
                .skipLast(1, TimeUnit.SECONDS) // 跳过最后1s发送的数据
                .subscribe(new Consumer<Long>() {

                    @Override
                    public void accept( Long along ) throws Exception {
                        Log.d(TAG,"获取到的整型事件元素是: "+ along);
                    }
                });
  • 测试结果
image.png

distinct() / distinctUntilChanged()

  • 作用
    过滤事件序列中重复的事件 / 连续重复的事件

  • 具体使用

// 使用1:过滤事件序列中重复的事件
        Observable.just(1, 2, 3, 1 , 2 )
                .distinct()
                .subscribe(new Consumer<Integer>() {
                      @Override
                      public void accept( Integer integer) throws Exception {
                          Log.d(TAG,"不重复的整型事件元素是: "+ integer);
                      }
        });

        // 使用2:过滤事件序列中 连续重复的事件
        // 下面序列中,连续重复的事件 = 3、4
        Observable.just(1,2,3,1,2,3,3,4,4 )
                .distinctUntilChanged()
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept( Integer integer) throws Exception {
                        Log.d(TAG,"不连续重复的整型事件元素是: "+ integer);
                    }
                });
  • 测试结果
示意图

3.2 根据 指定事件数量 过滤事件

  • 需求场景
    通过设置指定的事件数量,仅发送特定数量的事件

  • 对应操作符类型
    take() & takeLast()

  • 对应操作符使用

take()

  • 作用
    指定观察者最多能接收到的事件数量

  • 原理

示意图
  • 具体使用
Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                // 1. 发送5个事件
                emitter.onNext(1);
                emitter.onNext(2);
                emitter.onNext(3);
                emitter.onNext(4);
                emitter.onNext(5);
            }

            // 采用take()变换操作符
            // 指定了观察者只能接收2个事件
        }).take(2)
        .subscribe(new Observer<Integer>() {

            @Override
            public void onSubscribe(Disposable d) {
                Log.d(TAG, "开始采用subscribe连接");
            }

            @Override
            public void onNext(Integer value) {
                Log.d(TAG, "过滤后得到的事件是:"+ value  );
            }

            @Override
            public void onError(Throwable e) {
                Log.d(TAG, "对Error事件作出响应");
            }

            @Override
            public void onComplete() {
                Log.d(TAG, "对Complete事件作出响应");
            }
        });

// 实际上,可理解为:被观察者还是发送了5个事件,只是因为操作符的存在拦截了3个事件,最终观察者接收到的是2个事件
  • 测试结果
示意图

takeLast()

  • 作用
    指定观察者只能接收到被观察者发送的最后几个事件

  • 具体使用

Observable.just(1, 2, 3, 4, 5)
          .takeLast(3) //指定观察者只能接受被观察者发送的3个事件
          .subscribe(new Observer<Integer>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.d(TAG, "开始采用subscribe连接");
            }

            @Override
            public void onNext(Integer value) {
                Log.d(TAG, "过滤后得到的事件是:"+ value  );
            }

            @Override
            public void onError(Throwable e) {
                Log.d(TAG, "对Error事件作出响应");
            }

            @Override
            public void onComplete() {
                Log.d(TAG, "对Complete事件作出响应");
            }
        });
  • 测试结果
示意图

3.3 根据 指定时间 过滤事件

  • 需求场景
    通过设置指定的时间,仅发送在该时间内的事件

  • 对应操作符类型

示意图
  • 对应操作符使用

throttleFirst()/ throttleLast()

  • 作用
    在某段时间内,只发送该段时间内第1次事件 / 最后1次事件

如,1段时间内连续点击按钮,但只执行第1次的点击操作

  • 原理示意图
示意图
  • 具体使用
<<- 在某段时间内,只发送该段时间内第1次事件 ->>
Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                // 隔段事件发送时间
                e.onNext(1);
                Thread.sleep(500);
                
                e.onNext(2);
                Thread.sleep(400);
                
                e.onNext(3);
                Thread.sleep(300);
                
                e.onNext(4);
                Thread.sleep(300);
                
                e.onNext(5);
                Thread.sleep(300);
                
                e.onNext(6);
                Thread.sleep(400);
                
                e.onNext(7);
                Thread.sleep(300);
                e.onNext(8);

                Thread.sleep(300);
                e.onNext(9);

                Thread.sleep(300);
                e.onComplete();
            }
        }).throttleFirst(1, TimeUnit.SECONDS)//每1秒中采用数据
                .subscribe(new Observer<Integer>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        Log.d(TAG, "开始采用subscribe连接");
                    }

                    @Override
                    public void onNext(Integer value) {
                        Log.d(TAG, "接收到了事件"+ value  );
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d(TAG, "对Error事件作出响应");
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "对Complete事件作出响应");
                    }
                });


<<- 在某段时间内,只发送该段时间内最后1次事件 ->>
Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                // 隔段事件发送时间
                e.onNext(1);
                Thread.sleep(500);

                e.onNext(2);
                Thread.sleep(400);

                e.onNext(3);
                Thread.sleep(300);

                e.onNext(4);
                Thread.sleep(300);

                e.onNext(5);
                Thread.sleep(300);

                e.onNext(6);
                Thread.sleep(400);

                e.onNext(7);
                Thread.sleep(300);
                e.onNext(8);

                Thread.sleep(300);
                e.onNext(9);

                Thread.sleep(300);
                e.onComplete();
            }
        }).throttleLast(1, TimeUnit.SECONDS)//每1秒中采用数据
                .subscribe(new Observer<Integer>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        Log.d(TAG, "开始采用subscribe连接");
                    }

                    @Override
                    public void onNext(Integer value) {
                        Log.d(TAG, "接收到了事件"+ value  );
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d(TAG, "对Error事件作出响应");
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "对Complete事件作出响应");
                    }
                });


  • 测试结果
示意图

Sample()

  • 作用
    在某段时间内,只发送该段时间内最新(最后)1次事件

throttleLast() 操作符类似

  • 具体使用

仅需要把上文的 throttleLast() 改成Sample()操作符即可,此处不作过多描述

throttleWithTimeout () / debounce()

  • 作用
    发送数据事件时,若2次发送事件的间隔<指定时间,就会丢弃前一次的数据,直到指定时间内都没有新数据发射时才会发送后一次的数据

  • 具体使用

Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                // 隔段事件发送时间
                e.onNext(1);
                Thread.sleep(500);
                e.onNext(2); // 1和2之间的间隔小于指定时间1s,所以前1次数据(1)会被抛弃,2会被保留
                Thread.sleep(1500);  // 因为2和3之间的间隔大于指定时间1s,所以之前被保留的2事件将发出
                e.onNext(3);
                Thread.sleep(1500);  // 因为3和4之间的间隔大于指定时间1s,所以3事件将发出
                e.onNext(4);
                Thread.sleep(500); // 因为4和5之间的间隔小于指定时间1s,所以前1次数据(4)会被抛弃,5会被保留
                e.onNext(5);
                Thread.sleep(500); // 因为5和6之间的间隔小于指定时间1s,所以前1次数据(5)会被抛弃,6会被保留
                e.onNext(6);
                Thread.sleep(1500); // 因为6和Complete实践之间的间隔大于指定时间1s,所以之前被保留的6事件将发出

                e.onComplete();
            }
        }).throttleWithTimeout(1, TimeUnit.SECONDS)//每1秒中采用数据
                .subscribe(new Observer<Integer>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(Integer value) {
                        Log.d(TAG, "接收到了事件"+ value  );
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d(TAG, "对Error事件作出响应");
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "对Complete事件作出响应");
                    }
                });
  • 测试结果
示意图

3.4 根据 指定事件位置 过滤事件

  • 需求场景
    通过设置指定的位置,过滤在该位置的事件

  • 对应操作符类型

示意图
  • 对应操作符使用

firstElement() / lastElement()

  • 作用
    仅选取第1个元素 / 最后一个元素

  • 具体使用

// 获取第1个元素
        Observable.just(1, 2, 3, 4, 5)
                  .firstElement()
                  .subscribe(new Consumer<Integer>() {
                      @Override
                      public void accept( Integer integer) throws Exception {
                          Log.d(TAG,"获取到的第一个事件是: "+ integer);
                      }
        });

// 获取最后1个元素
        Observable.just(1, 2, 3, 4, 5)
                .lastElement()
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept( Integer integer) throws Exception {
                        Log.d(TAG,"获取到的最后1个事件是: "+ integer);
                    }
                });

  • 测试结果
示意图

elementAt()

  • 作用
    指定接收某个元素(通过 索引值 确定)

注:允许越界,即获取的位置索引 > 发送事件序列长度

  • 具体使用
// 使用1:获取位置索引 = 2的 元素
        // 位置索引从0开始
        Observable.just(1, 2, 3, 4, 5)
                  .elementAt(2)
                  .subscribe(new Consumer<Integer>() {
                      @Override
                      public void accept( Integer integer) throws Exception {
                          Log.d(TAG,"获取到的事件元素是: "+ integer);
                      }
        });

// 使用2:获取的位置索引 > 发送事件序列长度时,设置默认参数
        Observable.just(1, 2, 3, 4, 5)
                .elementAt(6,10)
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept( Integer integer) throws Exception {
                        Log.d(TAG,"获取到的事件元素是: "+ integer);
                    }
                });
  • 测试结果
示意图

elementAtOrError()

  • 作用
    elementAt()的基础上,当出现越界情况(即获取的位置索引 > 发送事件序列长度)时,即抛出异常

  • 具体使用

Observable.just(1, 2, 3, 4, 5)
                .elementAtOrError(6)
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept( Integer integer) throws Exception {
                        Log.d(TAG,"获取到的事件元素是: "+ integer);
                    }
                });
  • 测试结果
示意图

至此,RxJava2中常用的过滤操作符讲解完毕。


4. 实际开发需求案例

  • 在实际开发中,常见的过滤操作符实际需求场景有:功能防抖 & 联想搜索请求优化
  • 下面,我将通过具体实例来讲解上述2个需求

4.1 功能防抖

  • 需求场景
示意图

4.2 联想搜索优化

  • 场景说明
示意图

5. Demo地址

上述所有的Demo源代码都存放在:Carson_Ho的Github地址:RxJava2_过滤操作符


6. 总结

  • 下面,我将用一张图总结 RxJava2 中常用的条件 / 布尔操作符
示意图
  • Carson带你学RxJava系列文章:

入门
Carson带你学Android:这是一篇清晰易懂的Rxjava入门教程
Carson带你学Android:面向初学者的RxJava使用指南
Carson带你学Android:RxJava2.0到底更新了什么?
原理
Carson带你学Android:图文解析RxJava原理
Carson带你学Android:手把手带你源码分析RxJava
使用教程:操作符
Carson带你学Android:RxJava操作符教程
Carson带你学Android:RxJava创建操作符
Carson带你学Android:RxJava功能性操作符
Carson带你学Android:RxJava过滤操作符
Carson带你学Android:RxJava组合/合并操作符
Carson带你学Android:RxJava变换操作符
Carson带你学Android:RxJava条件/布尔操作符
实战
Carson带你学Android:什么时候应该使用Rxjava?(开发场景汇总)
Carson带你学Android:RxJava线程控制(含实例讲解)
Carson带你学Android:图文详解RxJava背压策略
Carson带你学Android:RxJava、Retrofit联合使用汇总(含实例教程)
Carson带你学Android:优雅实现网络请求嵌套回调
Carson带你学Android:网络请求轮询(有条件)
Carson带你学Android:网络请求轮询(无条件)
Carson带你学Android:网络请求出错重连(结合Retrofit)
Carson带你学Android:合并数据源
Carson带你学Android:联想搜索优化
Carson带你学Android:功能防抖
Carson带你学Android:从磁盘/内存缓存中获取缓存数据
Carson带你学Android:联合判断


欢迎关注Carson_Ho的简书

不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度


请点赞!因为你的鼓励是我写作的最大动力!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342