RxJava----操作符:过滤操作符

Filtering Observables(过滤操作符)

到目前为止我们看到的示例都很简单。你也可以用 Rx 来处理大批量实时数据,但是如果把所有大批量数据整个打包发给你的话,使用 Rx 还有啥优势呢? 本节 我们将介绍一些操作符(operators )来过滤数据、或者把所有数据变成一个需要的数据。

如果你了解过函数式编程(functional programming)或者 Java 中的 Stream,则本节介绍的操作函数是非常眼熟的。

本节中所有的操作符都返回一个不影响前一个 Observable 的新 Observable。 整个 Rx 框架都遵守该原则。通过创建新的 Observable 来转换之前的 Observable而不会对之前的 Observable造成干扰。

订阅到初始 Observable 的 Subscribers 不会受到任何影响,但是在后面的章节中也会看到,开发者也需要当心该原则。

Marble diagrams(弹子图)

你可以想象一个机器,不停的发射弹子出来,发射出来的弹子可以被其他模块再次加工(比如 上色、把不合格的弹子给回收了),加工完成后再次发射出来 … 弹子图就是对这个机器的抽象描述。在 Rx 中流行使用这种方式来描述操作符,毕竟图片看起来直观多了。 Marble diagrams(弹子图)基本元素如下:

RxJava----操作符:过滤操作符_第1张图片

时间从左往右流动,每个图形代表一个数据,竖线代表发射完成了,而 X 代表出现错误了。 操作函数把上面的 Observable 转换下面的新的 Observable , 里面的每个数据都被操作函数给处理了并返回一个新的数据。

filter

filter 函数使用一个 predicate 函数接口来判断每个发射的值是否能通过这个判断。如果返回 true,则该数据继续往下一个(过滤后的) Observable 发射。

RxJava----操作符:过滤操作符_第2张图片
比如下面示例创建了一个发射 0 到 9 十个数字的 源Observable。在该 Observable 使用一个 filter 操作来过滤掉奇数,最后只保留偶数。

        Observable values = Observable.range(0,10);
        Subscription oddNumbers = values
                .filter(new Func1() {
                    @Override
                    public Boolean call(Integer integer) {
                        return integer%2==0;
                    }
                })
                .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Integer integer) {
                        log(integer+"");
                    }
                });

结果:

0
2
4
6
8
Complete!

elementAt

  • 从特定的位置选择一个数据发射。

RxJava----操作符:过滤操作符_第3张图片

    Observable values = Observable.range(0, 10);
    Subscription subscription = values
        .elementAt(3)
        .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Integer integer) {
                        log(integer+"");
                    }
                });

结果:

3
Complete!
  • 该函数和访问数组或者集合类似,如果 Observable 发射的数据个数没有这么多,则会抛出 java.lang.IndexOutOfBoundsException 。
  • 可以使用一个默认值(elementAtOrDefault)来避免抛出 java.lang.IndexOutOfBoundsException。
        Observable values = Observable.range(0, 10);
        Subscription subscription = values
                .elementAtOrDefault(11, 5)
                .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Integer integer) {
                        log(integer+"");
                    }
                });

结果:

5
Complete!

sample

  • 在一个指定的时间间隔里由Observable发射最近一次的数值
  • 如果我们想让它定时发射第一个元素而不是最近的一个元素,我们可以使用throttleFirst()。
    RxJava----操作符:过滤操作符_第4张图片
        Observable.interval(150, TimeUnit.MILLISECONDS)
                .sample(1, TimeUnit.SECONDS)
                .subscribe(new Action1() {
                    @Override
                    public void call(Long aLong) {
                        log(aLong.toString());
                    }
                });

结果:

5
12
18
...

每个时间窗口的时间长短也可以不同。你一样可以使用一个信号 Observable 来指定时间结束:
RxJava----操作符:过滤操作符_第5张图片
下面这个示例和前面的效果是一样的

        Observable.interval(150, TimeUnit.MILLISECONDS)
                .sample(Observable.interval(1, TimeUnit.SECONDS))
                .subscribe(new Action1() {
                    @Override
                    public void call(Long aLong) {
                        log(aLong.toString());
                    }
                });

throttleFirst

throttleFirst 操作函数接收到第一个数据后,就开启一个时间窗口,在规定的时间窗口内发射第一个数据,后面的数据丢弃直到时间窗口结束。当时间窗口结束后,下一个数据发射后将开启下一个时间窗口。
RxJava----操作符:过滤操作符_第6张图片

        Observable.interval(150, TimeUnit.MILLISECONDS)
                .throttleFirst(1, TimeUnit.SECONDS)
                .subscribe(new Action1() {
                    @Override
                    public void call(Long aLong) {
                        log(aLong.toString());
                    }
                });

结果:

0
7
14
...

上面示例中,interval 每隔150ms 发射一个数据。从数据流开始,每个数值都是在 (i+1)*150ms 的时候发射。第一个数据 0 在 第150ms 发射,然后后面 1000ms 时间内发射的所有数据都被丢弃了。然后下一个数据在第1200ms 的时候发射,然后后面 1000ms 的数据又被丢弃。

throttleLast

throttleFirst 是根据第一个数据发射后来计时的,而 throttleLast 则使用一样的时间来分割数据流,发射每个时间窗口内的最后一个数据。
RxJava----操作符:过滤操作符_第7张图片

        Observable.interval(150, TimeUnit.MILLISECONDS)
                .throttleLast(1, TimeUnit.SECONDS)
                .subscribe(new Action1() {
                    @Override
                    public void call(Long aLong) {
                        log(aLong.toString());
                    }
                });

结果:

5
12
18
... 

上面示例中,当数据流开始的时候,时间窗口也同时开始了。第一个时间窗口在 1000ms 的时候结束,最后一个数据在 900ms的时候产生;下一个时间窗口从 1000ms 到 2000ms,这个时间窗口内的最后一个数据在 1950ms 的时候产生;同样可以计算下一个时间窗口内的数据在 2850ms 的时候产生。

debounce

debounce 操作函数的功能是:当一个数据发射的时候,就开始一个时间窗口计时,当这个时间窗口结束了还没有新的数据发射,则就发射这数据。如果在这个时间窗口内,又发射了一个新的数据,则当前数据丢弃,从新开始时间窗口计时。

  • 下图展示了多久从Observable发射一次新的数据,debounce()函数开启一个内部定时器,如果在这个时间间隔内没有新的数据发射,则新的Observable发射出最后一个数据:
    RxJava----操作符:过滤操作符_第8张图片
        Observable.create(new Observable.OnSubscribe() {
            @Override
            public void call(Subscribersuper Integer> subscriber) {
                if(subscriber.isUnsubscribed()) return;
                try {
                    //产生结果的间隔时间分别为100、200、300...900毫秒
                    for (int i = 1; i < 10; i++) {
                        subscriber.onNext(i);
                        Thread.sleep(i * 100);
                    }
                    subscriber.onCompleted();
                }catch(Exception e){
                    subscriber.onError(e);
                }
            }
        }).subscribeOn(Schedulers.newThread())
                .debounce(500, TimeUnit.MILLISECONDS)  //超时时间为400毫秒
                .subscribe(
                        new Action1() {
                            @Override
                            public void call(Integer integer) {
                                log("Next:" + integer);
                            }
                        }, new Action1() {
                            @Override
                            public void call(Throwable throwable) {
                                log("Error:" + throwable.getMessage());
                            }
                        }, new Action0() {
                            @Override
                            public void call() {
                                log("Complete!");
                            }
                        });

结果:

Next:5
Next:6
Next:7
Next:8
Next:9
completed!

简单来说,就是过滤掉发送间隔小于debounce(long timeout, TimeUnit unit)中timeout的数据。
比如说:timeout是300ms,在300ms以内如果有新数据,则不发送,重新开启计时,之前的数据抛弃掉,如果超过300ms,没有新数据,则发送最后一个数据(也就是最近一个开启计时的数据)。

ignoreElements

ignoreElements 会忽略所有发射的数据,只让 onCompleted 和 onError 可以通过。

    Observable values = Observable.range(0, 10);
    Subscription subscription = values
        .ignoreElements()
        .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Integer integer) {
                        log(integer+"");
                    }
                });

结果:

Complete!
ignoreElements() 和使用 filter(v -> false) 是一样的效果。

take和skip

  • 下面两个操作函数依据发射数据的索引来在特定的位置切断数据流,可以从头开始切断也可以从末尾开始切断。
  • take 从头开始获取前 N 个数据,而 skip 则是从头开始 跳过 N 个数据。注意,如果发射的数据比 N 小,则这两个函数都会发射一个 error。

take和skip

take

当我们不需要整个序列时,而是只想取开头或结尾的几个元素,我们可以用take()或takeLast()。

Observable   take(int num)

RxJava----操作符:过滤操作符_第9张图片

        Observable<Integer> values = Observable.range(0, 5);
        Subscription first2 = values
                .take(2)
                .subscribe(
                        new Action1<Integer>() {
                            @Override
                            public void call(Integer integer) {
                                log(integer+"");
                            }
                        }
                );    

结果:

0
1

只要第 N 个数据可用, take 操作就结束了,立即执行onCompleted()。 如果在 N 个数据发射之前发生了 error, error 信息会继续传递到下一个 Observable。 如果 第 N 个数据发射后, take 就不再关心源 Observable 的状态了。
—————这里的额状态————-

        Observable values = Observable.create(new Observable.OnSubscribe() {
            @Override
            public void call(Subscribersuper Integer> subscriber) {
                subscriber.onNext(1);
                subscriber.onError(new Exception("Oops"));
                subscriber.onNext(2);
            }
        });
        Subscription subscription = values
            .take(1)
            .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Integer integer) {
                        log(integer+"");
                    }
                });  

结果:

1
Complete!

如果将take(1)改成take(2):
结果:

1
Oops

skip

skip 返回 take 操作忽略的另外一部分数据。也就是跳过前面 N 个数据。

Observable   skip(int num)

RxJava----操作符:过滤操作符_第10张图片

    Observable values = Observable.range(0, 5); 
    Subscription subscription = values
        .skip(2)
        .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Integer integer) {
                        log(integer+"");
                    }
                });

结果:

2
3
4
Complete!

take和skip的重载

除了根据发射数据的索引来过滤数据以外,还可以使用数据流发射的时间来过滤。比如过滤掉前五秒发射的数据。

Observable   take(long time, java.util.concurrent.TimeUnit unit)
Observable   skip(long time, java.util.concurrent.TimeUnit unit)
        Observable values = Observable.interval(100, TimeUnit.MILLISECONDS);
        Subscription subscription = values
                .take(250, TimeUnit.MILLISECONDS)
                .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Long aLong) {
                        log(aLong+"");
                    }
                });

结果:

0
1
Complete!

上面示例中只获取前 250 毫秒发射的数据。 第 300 毫秒才开始发射数据 2, 所以这里只获取 0 和1 两个数据。

takeLast和skipLast

skip 和 take 是从头开始索引数据,而 skipLast 和 takeLast 和他们相反,是从末尾开始索引数据。
    Observable values = Observable.range(0, 5); 
    Subscription subscription = values
        .skipLast(2)
        .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Integer integer) {
                        log(integer+"");
                    }
                });

结果:

0
1
2
Complete!
同样这两个函数也有依时间为条件的重载函数。

takeWhile和skipWhile

这两个函数是使用一个 predicate 参数来当做判断条件。 如果判断条件返回为 ture, 则 takeWhile 保留该数据。
Observable<T>   takeWhile(Func1 super T,java.lang.Boolean> predicate)
        Observable values = Observable.interval(100, TimeUnit.MILLISECONDS);
        Subscription subscription = values
                .takeWhile(new Func1() {
                    @Override
                    public Boolean call(Long aLong) {
                        return aLong<2;
                    }
                })
                .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Long aLong) {
                        log(aLong+"");
                    }
                });

结果:

0
1
Complete!
不出意料, skipWhile 跳过过滤条件为 true 的数据。
        Observable values = Observable.interval(100, TimeUnit.MILLISECONDS);
        Subscription subscription = values
                .skipWhile(new Func1() {
                    @Override
                    public Boolean call(Long aLong) {
                        return aLong<2;
                    }
                })
                .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Long aLong) {
                        log(aLong+"");
                    }
                });

结果:

2
3
4
...

takeUntil 和 skipUntil

  • takeUntil 和 skipUntil 这两个函数和 takeWhile 、skipWhile 刚好相反。 当判断条件为 false 的时候, takeUntil 保留该数据。
  • takeUntil 和 skipUntil 还有另外一种不一样的重载函数。切断的条件为 另外一个 Observable 发射数据的时刻。
// 获取源Observable的数据直到 other Observable 发射第一个数据时停止
public final  Observable takeUntil(Observable other)

RxJava----操作符:过滤操作符_第11张图片

    Observable values = Observable.interval(100,TimeUnit.MILLISECONDS);
    Observable cutoff = Observable.timer(250, TimeUnit.MILLISECONDS); 
    Subscription subscription = values
        .takeUntil(cutoff)
        .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Long aLong) {
                        log(aLong+"");
                    }
                });

结果:

0
1
Complete!
  • 你应该还记得,这个 timer 函数会等待 250 毫秒然后发射一个数据。
  • 当 takeUntil 收到 这个数据的时候就停止继续接受 values 发射的数据。
  • cutoff 这个充当信号的 Observable 可以是任意数据类型的,这里不关心数据只关心何时发射了数据。
  • skipUntil 也是一样,当收到另外一个 Observable 发射数据的时候,就开始接收 源 Observable 的数据。
    Observable values = Observable.interval(100,TimeUnit.MILLISECONDS);
    Observable cutoff = Observable.timer(250, TimeUnit.MILLISECONDS);
    Subscription subscription = values
        .skipUntil(cutoff)
        .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        log("Complete!");
                    }
                    @Override
                    public void onError(Throwable e) {
                        log(e.getMessage().toString());
                    }
                    @Override
                    public void onNext(Long aLong) {
                        log(aLong+"");
                    }
                });

结果:

2
3
4
...

first and last

first()方法和last()方法很容易弄明白。它们从Observable中只发射第一个元素或者最后一个元素。这两个都可以传Func1作为参数,:一个可以确定我们感兴趣的第一个或者最后一个的方法:

RxJava----操作符:过滤操作符_第12张图片

RxJava----操作符:过滤操作符_第13张图片

 与first()和last()相似的变量有:firstOrDefault()和lastOrDefault().这两个函数当可观测序列完成时不再发射任何值时用得上。在这种场景下,如果Observable不再发射任何值时我们可以指定发射一个默认的值

distinct 和 distinctUntilChanged

distinct 函数用来过滤掉已经出现过的数据了。

    public final Observable distinct()
    public final  Observable distinct(Func1super T,? extends U> keySelector)

distinct 还有个变体是 distinctUntilChanged。区别是 distinctUntilChanged 只过滤相邻的 key 一样的数据。

    public final Observable distinctUntilChanged()
    public final  Observable distinctUntilChanged(Func1super T,? extends U> keySelector)

distinct()

RxJava----操作符:过滤操作符_第14张图片

        Observable<Integer> values = Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber super Integer> subscriber) {
                subscriber.onNext(1);
                subscriber.onNext(1);
                subscriber.onNext(2);
                subscriber.onNext(3);
                subscriber.onNext(2);
                subscriber.onCompleted();
            }
        });
        Subscription subscription = values
                .distinct()
                .subscribe(
                        new Action1<Integer>() {
                            @Override
                            public void call(Integer integer) {
                                log(integer + "");
                            }
                        }
                );

结果:

1
2
3

distinct(keySelector)

distinct 还有一个重载函数,该函数有个生成 key 的参数。每个发射的数据都使用该参数生成一个 key,然后使用该key 来判断数据是否一样。

RxJava----操作符:过滤操作符_第15张图片

        Observable values = Observable.create(new Observable.OnSubscribe() {
            @Override
            public void call(Subscribersuper String> subscriber) {
                subscriber.onNext("First");
                subscriber.onNext("Second");
                subscriber.onNext("Third");
                subscriber.onNext("Fourth");
                subscriber.onNext("Fifth");
                subscriber.onCompleted();
            }
        });
        Subscription subscription = values
                .distinct(new Func1() {
                    @Override
                    public Object call(String s) {
                        return s.charAt(0);
                    }
                })
                .subscribe(
                        new Action1() {
                            @Override
                            public void call(String s) {
                                log(s);
                            }
                        }
                );

结果:

First
Second
Third
“Fourth” 和 “Fifth” 字符串被过滤掉了,应为他们的 key (首字母)和 First 一样。已经发射过的数据将被过滤掉。
有经验的码农知道,该函数在内部维护一个 key 集合来保存所有已经发射数据的 key,当有新的数据发射的时候,在集合中查找该 数据的key 是否存在。 在使用 Rx 操作函数的时把内部细节给封装起来了,但是我们应该注意该问题来避免性能问题。(如果有大量的数据,维护一个内部的集合来保存 key 可能会占用很多内存。)

distinctUntilChanged

RxJava----操作符:过滤操作符_第16张图片

        Observable<Integer> values = Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber super Integer> subscriber) {
                subscriber.onNext(1);
                subscriber.onNext(1);
                subscriber.onNext(2);
                subscriber.onNext(3);
                subscriber.onNext(2);
                subscriber.onCompleted();
            }
        });
        Subscription subscription = values
                .distinctUntilChanged()
                .subscribe(
                        new Action1<Integer>() {
                            @Override
                            public void call(Integer integer) {
                                log(integer + "");
                            }
                        }
                );

结果:

1
2
3
2

distinctUntilChanged(keySelector)

        Observable values = Observable.create(new Observable.OnSubscribe() {
            @Override
            public void call(Subscribersuper String> subscriber) {
                subscriber.onNext("First");
                subscriber.onNext("Second");
                subscriber.onNext("Third");
                subscriber.onNext("Fourth");
                subscriber.onNext("Fifth");
                subscriber.onCompleted();
            }
        });
        Subscription subscription = values
                .distinctUntilChanged(new Func1() {
                    @Override
                    public Object call(String s) {
                        return s.charAt(0);
                    }
                })
                .subscribe(
                        new Action1() {
                            @Override
                            public void call(String s) {
                                log(s);
                            }
                        }
                );

结果:

First
Second
Third
Fourth

项目源码 GitHub求赞,谢谢!
引用:
RxJava 教程第二部分:事件流基础之 过滤数据 - 云在千峰
过滤Observables | RxJava Essentials CN
Android RxJava使用介绍(三) RxJava的操作符 - 呼啸而过的专栏 - 博客频道 - CSDN.NET

你可能感兴趣的:(开源框架)