Observable<GroupedObservable<Integer, Integer>> observable = Observable.concat(
Observable.range(1,4), Observable.range(1,6)).groupBy(integer -> integer);
Observable.concat(observable).subscribe(integer -> System.out.println("groupBy : " + integer));
该方法有多个重载版本,这里我们用到的一个的定义是:
public final <K> Observable<GroupedObservable<K, T>> groupBy(Function<? super T, ? extends K> keySelector)
6.scan
scan
操作符对原始Observable发射的第一项数据应用一个函数,然后将那个函数的结果作为自己的第一项数据发射。它将函数的结果同第二项数据一起填充给这个函数来产生它自己的第二项数据。它持续进行这个过程来产生剩余的数据序列。这个操作符在某些情况下被叫做accumulator。
以下面的程序为例,该程序的输结果是2 6 24 120 720
,可以看出这里的计算规则是,我们把传入到scan
中的函数记为f
,序列记为x
,生成的序列记为y
,那么这里的计算公式是y(0)=x(0); y(i)=f(y(i-1), x(i)), i>0
:
Observable.range(2, 5).scan((i1, i2) -> i1 * i2).subscribe(i -> System.out.print(i + " "));
除了上面的这种形式,scan
方法还有一个重载的版本,我们可以使用这个版本的方法来在生成序列的时候指定一个初始值。以下面的程序为例,它的输出结果是3 6 18 72 360 2160
,可以看出它的输出比上面的形式多了1个,这是因为当指定了初始值之后,生成的第一个数字就是那个初始值,剩下的按照我们上面的规则进行的。所以,用同样的函数语言来描述的话,那么它就应该是下面的这种形式:y(0)=initialValue; y(i)=f(y(i-1), x(i)), i>0
。
Observable.range(2, 5).scan(3, (i1, i2) -> i1 * i2).subscribe(i -> System.out.print(i + " "));
以上方法的定义是:
public final Observable<T> scan(BiFunction<T, T, T> accumulator)
public final <R> Observable<R> scan(R initialValue, BiFunction<R, ? super T, R> accumulator)
7.window
window
Window和Buffer类似,但不是发射来自原始Observable的数据包,它发射的是Observable,这些Observables中的每一个都发射原始Observable数据的一个子集,最后发射一个onCompleted通知。
以下面的程序为例,这里我们首先生成了一个由10个数字组成的整数序列,然后使用window
函数将它们每3个作为一组,每组会返回一个对应的Observable对象。 这里我们对该返回的结果进行订阅并进行消费,因为10个数字,所以会被分成4个组,每个对应一个Observable:
Observable.range(1, 10).window(3).subscribe(
observable -> observable.subscribe(integer -> System.out.println(observable.hashCode() + " : " + integer)));
除了对数据包进行分组,我们还可以根据时间来对发射的数据进行分组。该方法有多个重载的版本,这里我们给出其中的比较具有代表性的几个:
public final Observable<Observable<T>> window(long count)
public final Observable<Observable<T>> window(long timespan, long timeskip, TimeUnit unit)
public final <B> Observable<Observable<T>> window(ObservableSource<B> boundary)
public final <B> Observable<Observable<T>> window(Callable<? extends ObservableSource<B>> boundary)
2.1.3 过滤操作
1.filter
filter
用来根据指定的规则对源进行过滤,比如下面的程序用来过滤整数1到10中所有大于5的数字:
Observable.range(1,10).filter(i -> i > 5).subscribe(System.out::println);
下面是该方法的定义:
public final Observable<T> filter(Predicate<? super T> predicate)
2.elementAt & firstElement & lastElement
elementAt
用来获取源中指定位置的数据,它有几个重载方法,这里我们介绍一下最简单的一个方法的用法。下面是elementAt
的一个示例,它将获取源数据中索引为1的元素并交给观察者订阅。下面的程序将输出1
Observable.range(1, 10).elementAt(0).subscribe(System.out::print);
这里我们给出elementAt
及其相关的方法的定义,它们的使用相似。注意一下这里的返回类型:
public final Maybe<T> elementAt(long index)
public final Single<T> elementAt(long index, T defaultItem)
public final Single<T> elementAtOrError(long index)
除了获取指定索引的元素的方法之外,RxJava中还有可以用来直接获取第一个和最后一个元素的方法,这里我们直接给出方法的定义:
public final Maybe<T> firstElement()
public final Single<T> first(T defaultItem)
public final Single<T> firstOrError()
public final Maybe<T> lastElement()
public final Single<T> last(T defaultItem)
public final Single<T> lastOrError()
3.distinct & distinctUntilChanged
distinct
用来对源中的数据进行过滤,以下面的程序为例,这里会把重复的数字7过滤掉:
Observable.just(1,2,3,4,5,6,7,7).distinct().subscribe(System.out::print);
与之类似的还有distinctUntilChanged
方法,与distinct
不同的是,它只当相邻的两个元素相同的时候才会将它们过滤掉。比如下面的程序会过滤掉其中的2和5,所以最终的输出结果是12345676
:
Observable.just(1,2,2,3,4,5,5,6,7,6).distinctUntilChanged().subscribe(System.out::print);
该方法也有几个功能相似的方法,这里给出它们的定义如下:
public final Observable<T> distinct()
public final <K> Observable<T> distinct(Function<? super T, K> keySelector)
public final <K> Observable<T> distinct(Function<? super T, K> keySelector, Callable<? extends Collection<? super K>> collectionSupplier)
public final Observable<T> distinctUntilChanged()
public final <K> Observable<T> distinctUntilChanged(Function<? super T, K> keySelector)
public final Observable<T> distinctUntilChanged(BiPredicate<? super T, ? super T> comparer)
4.skip & skipLast & skipUntil & skipWhile
skip
方法用于过滤掉数据的前n项,比如下面的程序将会过滤掉前2项,因此输出结果是345
:
Observable.range(1, 5).skip(2).subscribe(System.out::print);
与skip
方法对应的是take
方法,它用来表示只选择数据源的前n项,该方法的示例就不给出了。这里,我们说一下与之类功能类似的重载方法。skip
还有一个重载方法接受两个参数,用来表示跳过指定的时间,也就是在指定的时间之后才开始进行订阅和消费。下面的程序会在3秒之后才开始不断地输出数字:
Observable.range(1,5).repeat().skip(3, TimeUnit.SECONDS).subscribe(System.out::print);
与skip
功能相反的方法的还有skipLast
,它用来表示过滤掉后面的几项,以及最后的一段时间不进行发射等。比如下面的方法,我们会在程序开始之前进行计时,然后会不断重复输出数字,直到5秒之后结束。然后,我们用skipLast
方法表示最后的2秒不再进行发射。所以下面的程序会先不断输出数字3秒,3秒结束后停止输出,并在2秒之后结束程序:
long current = System.currentTimeMillis();
Observable.range(1,5)
.repeatUntil(() -> System.currentTimeMillis() - current > TimeUnit.SECONDS.toMillis(5))
.skipLast(2, TimeUnit.SECONDS).subscribe(System.out::print);
与上面的这些方法类似的还有一些,这里我们不再一一列举。因为这些方法的重载方法比较多,下面我们给出其中的具有代表性的一部分:
public final Observable<T> skip(long count)
public final Observable<T> skip(long time, TimeUnit unit, Scheduler scheduler)
public final Observable<T> skipLast(int count)
public final Observable<T> skipLast(long time, TimeUnit unit, Scheduler scheduler, boolean delayError, int bufferSize)
public final <U> Observable<T> skipUntil(ObservableSource<U> other)
public final Observable<T> skipWhile(Predicate<? super T> predicate)
5.take & takeLast & takeUntil & takeWhile
与skip
方法对应的是take
方法,它表示按照某种规则进行选择操作。我们以下面的程序为例,这里第一段程序表示只发射序列中的前2个数据:
Observable.range(1, 5).take(2).subscribe(System.out::print);
下面的程序表示只选择最后2秒中输出的数据:
long current = System.currentTimeMillis();
Observable.range(1,5)
.repeatUntil(() -> System.currentTimeMillis() - current > TimeUnit.SECONDS.toMillis(5))
.takeLast(2, TimeUnit.SECONDS).subscribe(System.out::print);
下面是以上相关的方法的定义,同样的,我们只选择其中比较有代表性的几个:
public final Observable<T> take(long count)
public final Observable<T> takeLast(long count, long time, TimeUnit unit, Scheduler scheduler, boolean delayError, int bufferSize)
public final <U> Observable<T> takeUntil(ObservableSource<U> other)
public final Observable<T> takeUntil(Predicate<? super T> stopPredicate)
public final Observable<T> takeWhile(Predicate<? super T> predicate)
6.ignoreElements
该方法用来过滤所有源Observable产生的结果,只会把Observable的onComplete和onError事件通知给订阅者。下面是该方法的定义:
public final Completable ignoreElements()
7.throttleFirst & throttleLast & throttleLatest & throttleWithTimeout
这些方法用来对输出的数据进行限制,它们是通过时间的”窗口“来进行限制的,你可以理解成按照指定的参数对时间进行分片,然后根据各个方法的要求选择第一个、最后一个、最近的等进行发射。下面是throttleLast
方法的用法示例,它会输出每个500毫秒之间的数字中最后一个数字:
Observable.interval(80, TimeUnit.MILLISECONDS)
.throttleLast(500, TimeUnit.MILLISECONDS)
.subscribe(i -> System.out.print(i + " "));
其他的几个方法的功能大致列举如下:
throttleFirst
只会发射指定的Observable在指定的事件范围内发射出来的第一个数据;throttleLast
只会发射指定的Observable在指定的事件范围内发射出来的最后一个数据;throttleLatest
用来发射距离指定的时间分片最近的那个数据;throttleWithTimeout
仅在过了一段指定的时间还没发射数据时才发射一个数据,如果在一个时间片达到之前,发射的数据之后又紧跟着发射了一个数据,那么这个时间片之内之前发射的数据会被丢掉,该方法底层是使用debounce
方法实现的。如果数据发射的频率总是快过这里的timeout
参数指定的时间,那么将不会再发射出数据来。
下面是这些方法及其重载方法的定义(选择其中一部分):
public final Observable<T> throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler)
public final Observable<T> throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler)
public final Observable<T> throttleLatest(long timeout, TimeUnit unit, Scheduler scheduler, boolean emitLast)
public final Observable<T> throttleWithTimeout(long timeout, TimeUnit unit, Scheduler scheduler)
8.debounce
debounce
也是用来限制发射频率过快的,它仅在过了一段指定的时间还没发射数据时才发射一个数据。我们通过下面的图来说明这个问题:
这里红、绿、蓝三个球发射出来的原因都是因为当反射了这个球之后的一定的时间内没有其他的球发射出来,这个时间是我们可以通过参数来指定的。
该方法的用法与throttle
之类的方法类似,上面也说过throttle
那些方法底层用了debounce
实现,所以,这里我们不再为该方法专门编写相关的测试代码。
9.sample
实际上throttleLast
的实现中内部调用的就是sample
。
2.1.4 组合操作
1.startWith & startWithArray
startWith
方法可以用来在指定的数据源的之前插入几个数据,它的功能类似的方法有startWithArray
,另外还有几个重载方法。这里我们给出一个基本的用法示例,下面的程序会在原始的数字流1-5的前面加上0,所以最终的输出结果是012345
:
Observable.range(1,5).startWith(0).subscribe(System.out::print);
下面是startWith
及其几个功能相关的方法的定义:
public final Observable<T> startWith(Iterable<? extends T> items)
public final Observable<T> startWith(ObservableSource<? extends T> other)
public final Observable<T> startWith(T item)
public final Observable<T> startWithArray(T... items)
2.merge & mergeArray
merge
可以让多个数据源的数据合并起来进行发射,当然它可能会让merge
之后的数据交错发射。下面是一个示例,这个例子中,我们使用merge
方法将两个Observable
合并到了一起进行监听:
Observable.merge(Observable.range(1,5), Observable.range(6,5)).subscribe(System.out::print);
鉴于merge
方法及其功能类似的方法太多,我们这里挑选几个比较有代表性的方法,具体的可以查看RxJava的源代码:
public static <T> Observable<T> merge(Iterable<? extends ObservableSource<? extends T>> sources)
public static <T> Observable<T> mergeArray(ObservableSource<? extends T>... sources)
public static <T> Observable<T> mergeDelayError(Iterable<? extends ObservableSource<? extends T>> sources)
public static <T> Observable<T> mergeArrayDelayError(ObservableSource<? extends T>... sources)
这里的mergeError
方法与merge
方法的表现一致,只是在处理由onError
触发的错误的时候有所不同。mergeError
方法会等待所有的数据发射完毕之后才把错误发射出来,即使多个错误被触发,该方法也只会发射出一个错误信息。而如果使用merger
方法,那么当有错误被触发的时候,该错误会直接被抛出来,并结束发射操作。下面是该方法的一个使用的示例,这里我们主线程停顿4秒,然后所有merge
的Observable中的一个会在线程开始的第2秒的时候触发一个错误,该错误最终会在所有的数据发射完毕之后被发射出来:
Observable.mergeDelayError(Observable.range(1,5),
Observable.range(1,5).repeat(2),
Observable.create((ObservableOnSubscribe) observableEmitter -> {
Thread.sleep(2000);
observableEmitter.onError(new Exception(“error”));
})
).subscribe(System.out::print, System.out::print);
Thread.sleep(4000);
3.concat & concatArray & concatEager
该方法也是用来将多个Observable拼接起来,但是它会严格按照传入的Observable的顺序进行发射,一个Observable没有发射完毕之前不会发射另一个Observable里面的数据。下面是一个程序示例,这里传入了两个Observable,会按照顺序输出12345678910
:
Observable.concat(Observable.range(1, 5), Observable.range(6, 5)).subscribe(System.out::print);
下面是该方法的定义,鉴于该方法及其重载方法太多,这里我们选择几个比较有代表性的说明:
public static <T> Observable<T> concat(Iterable<? extends ObservableSource<? extends T>> sources)
public static <T> Observable<T> concatDelayError(Iterable<? extends ObservableSource<? extends T>> sources)
public static <T> Observable<T> concatArray(ObservableSource<? extends T>... sources)
public static <T> Observable<T> concatArrayDelayError(ObservableSource<? extends T>... sources)
public static <T> Observable<T> concatEager(ObservableSource<? extends ObservableSource<? extends T>> sources)
public static <T> Observable<T> concatArrayEager(ObservableSource<? extends T>... sources)
对于concat
方法,我们之前已经介绍过它的用法;这里的conactArray
的功能与之类似;对于concatEager
方法,当一个观察者订阅了它的结果,那么就相当于订阅了它拼接的所有ObservableSource
,并且会先缓存这些ObservableSource发射的数据,然后再按照顺序将它们发射出来。而对于这里的concatDelayError
方法的作用和前面的mergeDelayError
类似,只有当所有的数据都发射完毕才会处理异常。
4.zip & zipArray & zipIterable
zip
操作用来将多个数据项进行合并,可以通过一个函数指定这些数据项的合并规则。比如下面的程序的输出结果是6 14 24 36 50
,显然这里的合并的规则是相同索引的两个数据的乘积。不过仔细看下这里的输出结果,可以看出,如果一个数据项指定的位置没有对应的值的时候,它是不会参与这个变换过程的:
Observable.zip(Observable.range(1, 6), Observable.range(6, 5), (integer, integer2) -> integer * integer2)
.subscribe(i -> System.out.print(i + " "));
zip
除了用作两个 Observable 的合并,它还可以用来指定两个 Observable 的顺序:
Observable a = // … A 请求
Observable b = // … B 请求
Observable.zip(a, b, new BiFunction<String, Integer, Object>(){
@Override
public Object apply(@NonNull String s, @NonNull Integer integer) throws Exception {
// 拿到了 A 请求和 B 请求的第 n 次执行的结果
return new Object();
}
}).subscribe();
A 和 B 会并行在各自的子线程当中, 并且会合并到 apply()
方法中。它能保证 B 操作在 A 操作之前执行。我们可以使用这种方式来实现线程的控制。即当一个任务完成之后才执行另一个任务,同时它们的任务的结果可以被合并。那么合并的规则是什么呢?即那么如果 A 和 B 多次发送结果,也就是多次调用 onNext()
方法。此时,A 和 B 发送的结果会按照先后顺序配对,并回调上述的 BiFunction
函数。
zip
方法有多个重载的版本,同时也有功能近似的方法,这里我们挑选有代表性的几个进行说明:
public static <T, R> Observable<R> zip(Iterable<? extends ObservableSource<? extends T>> sources, Function<? super Object[], ? extends R> zipper)
ublic static <T, R> Observable<R> zipArray(Function<? super Object[], ? extends R> zipper, boolean delayError, int bufferSize, ObservableSource... sources)
public static <T, R> Observable<R> zipIterable(Iterable<? extends ObservableSource<? extends T>> sources, Function<? super Object[], ? extends R> zipper, boolean delayError, int bufferSize)
实际上上面几个方法的用法和功能基本类似,区别在于传入的ObservableSource
的参数的形式。
5.combineLastest
与zip
操作类似,但是这个操作的输出结果与zip
截然不同,以下面的程序为例,它的输出结果是36 42 48 54 60
:
Observable.combineLatest(Observable.range(1, 6), Observable.range(6, 5), (integer, integer2) -> integer * integer2)
.subscribe(i -> System.out.print(i + " "));
利用下面的这张图可以比较容易来说明这个问题:
上图中的上面的两条横线代表用于拼接的两个数据项,下面的一条横线是拼接之后的结果。combineLatest
的作用是拼接最新发射的两个数据。下面我们用上图的过程来说明该方法是如何执行的:开始第一条只有1的时候无法拼接,;当第二条出现A的时候,此时最新的数据是1和A,故组合成一个1A;第二个数据项发射了B,此时最新的数据是1和B,故组合成1B;第一条横线发射了2,此时最新的数据是2和B,因此得到了2B,依次类推。然后再回到我们上面的问题,第一个数据项连续发射了5个数据的时候,第二个数据项一个都没有发射出来,因此没有任何输出;然后第二个数据项开始发射数据,当第二个数据项发射了6的时候,此时最新的数据组合是6和6,故得36;然后,第二个数据项发射了7,此时最新的数据组合是6和7,故得42,依次类推。
该方法也有对应的combineLatestDelayError
方法,用途也是只有当所有的数据都发射完毕的时候才去处理错误逻辑。
2.1.5 辅助操作
1.delay
delay
方法用于在发射数据之前停顿指定的时间,比如下面的程序会在真正地发射数据之前停顿1秒:
Observable.range(1, 5).delay(1000, TimeUnit.MILLISECONDS).subscribe(System.out::print);
Thread.sleep(1500);
同样delay
方法也有几个重载的方法,可以供我们用来指定触发的线程等信息,这里给出其中的两个,其他的可以参考源码和文档:
public final Observable<T> delay(long delay, TimeUnit unit)
public final Observable<T> delay(long delay, TimeUnit unit, Scheduler scheduler)
2.do系列
RxJava中还有一系列的方法可以供我们使用,它们共同的特点是都是以do
开头,下面我们列举一下这些方法并简要说明一下它们各自的用途:
public final Observable<T> doAfterNext(Consumer<? super T> onAfterNext)
,会在onNext
方法之后触发;public final Observable<T> doAfterTerminate(Action onFinally)
,会在Observable终止之后触发;public final Observable<T> doFinally(Action onFinally)
,当onComplete
或者onError
的时候触发;public final Observable<T> doOnDispose(Action onDispose)
,当被dispose的时候触发;public final Observable<T> doOnComplete(Action onComplete)
,当complete的时候触发;public final Observable<T> doOnEach(final Observer<? super T> observer)
,当每个onNext
调用的时候触发;public final Observable<T> doOnError(Consumer<? super Throwable> onError)
,当调用onError
的时候触发;public final Observable<T> doOnLifecycle(final Consumer<? super Disposable> onSubscribe, final Action onDispose)
public final Observable<T> doOnNext(Consumer<? super T> onNext)
,,会在onNext
的时候触发;public final Observable<T> doOnSubscribe(Consumer<? super Disposable> onSubscribe)
,会在订阅的时候触发;public final Observable<T> doOnTerminate(final Action onTerminate)
,当终止之前触发。
这些方法可以看作是对操作执行过程的一个监听,当指定的操作被触发的时候会同时触发这些监听方法:
Observable.range(1, 5)
.doOnEach(integerNotification -> System.out.println("Each : " + integerNotification.getValue()))
.doOnComplete(() -> System.out.println(“complete”))
.doFinally(() -> System.out.println(“finally”))
.doAfterNext(i -> System.out.println("after next : " + i))
.doOnSubscribe(disposable -> System.out.println(“subscribe”))
.doOnTerminate(() -> System.out.println(“terminal”))
.subscribe(i -> System.out.println("subscribe : " + i));
3.subscribeOn & observeOn
subscribeOn
用于指定Observable自身运行的线程,observeOn
用于指定发射数据所处的线程,比如Android中的异步任务需要用subscribeOn
指定发射数据所在的线程是非主线程,然后执行完毕之后将结果发送给主线程,就需要用observeOn
来指定。比如下面的程序,我们用这两个方法来指定所在的线程:
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter) throws Exception {
// do nothing
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
4.timeout
用来设置一个超时时间,如果指定的时间之内没有任何数据被发射出来,那么就会执行我们指定的数据项。如下面的程序所示,我们先为设置了一个间隔200毫秒的数字产生器,开始发射数据之前要停顿1秒钟,因为我们设置的超时时间是500毫秒,因而在第500毫秒的时候会执行我们传入的数据项:
Observable.interval(1000, 200, TimeUnit.MILLISECONDS)
.timeout(500, TimeUnit.MILLISECONDS, Observable.rangeLong(1, 5))
.subscribe(System.out::print);
Thread.sleep(2000);
timeout
方法有多个重载方法,可以为其指定线程等参数,可以参考源码或者文档了解详情。
2.1.6 错误处理操作符
错误处理操作符主要用来提供给Observable,用来对错误信息做统一的处理,常用的两个是catch
和retry
。
1.catch
catch操作会拦截原始的Observable的onError
通知,将它替换为其他数据项或者数据序列,让产生的Observable能够正常终止或者根本不终止。在RxJava中该操作有3终类型:
onErrorReturn
:这种操作会在onError触发的时候返回一个特殊的项替换错误,并调用观察者的onCompleted方法,而不会将错误传递给观察者;onErrorResumeNext
:会在onError触发的时候发射备用的数据项给观察者;onExceptionResumeNext
:如果onError触发的时候onError收到的Throwable不是Exception,它会将错误传递给观察者的onError方法,不会使用备用的Observable。
下面是onErrorReturn
和onErrorResumeNext
的程序示例,这里第一段代码会在出现错误的时候输出666
,而第二段会在出现错误的时候发射数字12345
:
Observable.create((ObservableOnSubscribe) observableEmitter -> {
observableEmitter.onError(null);
observableEmitter.onNext(0);
}).onErrorReturn(throwable -> 666).subscribe(System.out::print);
Observable.create((ObservableOnSubscribe) observableEmitter -> {
observableEmitter.onError(null);
observableEmitter.onNext(0);
}).onErrorResumeNext(Observable.range(1,5)).subscribe(System.out::print);
2.retry
retry
使用了一种错误重试机制,它可以在出现错误的时候进行重试,我们可以通过参数指定重试机制的条件。以下面的程序为例,这里我们设置了当出现错误的时候会进行2次重试,因此,第一次的时候出现错误会调用onNext
,重试2次又会调用2次onNext
,第二次重试的时候因为重试又出现了错误,因此此时会触发onError
方法。也就是说,下面这段代码会触发onNext
3次,触发onError()
1次:
Observable.create(((ObservableOnSubscribe) emitter -> {
emitter.onNext(0);
emitter.onError(new Throwable(“Error1”));
emitter.onError(new Throwable(“Error2”));
})).retry(2).subscribe(i -> System.out.println("onNext : " + i), error -> System.out.print("onError : " + error));
retry
有几个重载的方法和功能相近的方法,下面是这些方法的定义(选取部分):
public final Observable<T> retry()
:会进行无限次地重试;public final Observable<T> retry(BiPredicate<? super Integer, ? super Throwable> predicate)
public final Observable<T> retry(long times)
:指定重试次数;public final Observable<T> retry(long times, Predicate<? super Throwable> predicate)
public final Observable<T> retryUntil(final BooleanSupplier stop)
public final Observable<T> retryWhen(Function<? super Observable<Throwable>, ? extends ObservableSource<?>> handler)
2.1.7 条件操作符和布尔操作符
1.all & any
all
用来判断指定的数据项是否全部满足指定的要求,这里的“要求”可以使用一个函数来指定;any
用来判断指定的Observable是否存在满足指定要求的数据项。
在下面的程序中,我们用该函数来判断指定的数据项是否全部满足大于5的要求,显然是不满足的,因此下面的程序将会输出false
:
Observable.range(5, 5).all(i -> i>5).subscribe(System.out::println); // false
Observable.range(5, 5).any(i -> i>5).subscribe(System.out::println); // true
以下是该方法的定义:
public final Single<Boolean> all(Predicate<? super T> predicate)
public final Single<Boolean> any(Predicate<? super T> predicate)
2.contains & isEmpty
这两个方法分别用来判断数据项中是否包含我们指定的数据项,已经判断数据项是否为空:
Observable.range(5, 5).contains(4).subscribe(System.out::println); // false
Observable.range(5, 5).isEmpty().subscribe(System.out::println); // false
以下是这两个方法的定义:
public final Single<Boolean> isEmpty()
public final Single<Boolean> contains(final Object element)
3.sequenceEqual
sequenceEqual
用来判断两个Observable发射出的序列是否是相等的。比如下面的方法用来判断两个序列是否相等:
Observable.sequenceEqual(Observable.range(1,5), Observable.range(1, 5)).subscribe(System.out::println);
4.amb
amb
作用的两个或多个Observable,但是只会发射最先发射数据的那个Observable的全部数据:
Observable.amb(Arrays.asList(Observable.range(1, 5), Observable.range(6, 5))).subscribe(System.out::print)
该方法及其功能近似的方法的定义,这里前两个是静态的方法,第二个属于实例方法:
public static <T> Observable<T> amb(Iterable<? extends ObservableSource<? extends T>> sources)
public static <T> Observable<T> ambArray(ObservableSource<? extends T>... sources)
public final Observable<T> ambWith(ObservableSource<? extends T> other)
5.defaultIfEmpty
defaultIfEmpty
用来当指定的序列为空的时候指定一个用于发射的值。下面的程序中,我们直接调用发射器的onComplete
方法,因此序列是空的,结果输出一个整数6
:
Observable.create((ObservableOnSubscribe) Emitter::onComplete).defaultIfEmpty(6).subscribe(System.out::print);
下面是该方法的定义:
public final Observable<T> defaultIfEmpty(T defaultItem)
2.1.8 转换操作符
1.toList & toSortedList
toList
和toSortedList
用于将序列转换成列表,后者相对于前者增加了排序的功能:
Observable.range(1, 5).toList().subscribe(System.out::println);
Observable.range(1, 5).toSortedList(Comparator.comparingInt(o -> -o)).subscribe(System.out::println);
下面是它们的定义,它们有多个重载版本,这里选择其中的两个进行说明:
public final Single<List<T>> toList()
public final Single<List<T>> toSortedList(final Comparator<? super T> comparator)
注意一下,这里的返回结果是Single
类型的,不过这并不妨碍我们继续使用链式操作,因为Single
的方法和Observable
基本一致。 另外还要注意这里的Single
中的参数是一个List<T>
,也就是说,它把整个序列转换成了一个列表对象。因此,上面的两个示例程序的输出是:
[1, 2, 3, 4, 5]
[5, 4, 3, 2, 1]
2.toMap & toMultimap
toMap
用于将发射的数据转换成另一个类型的值,它的转换过程是针对每一个数据项的。以下面的代码为例,它会将原始的序列中的每个数字转换成对应的十六进制。但是,toMap
转换的结果不一定是按照原始的序列的发射的顺序来的:
Observable.range(8, 10).toMap(Integer::toHexString).subscribe(System.out::print);
与toMap
近似的是toMultimap
方法,它可以将原始序列的每个数据项转换成一个集合类型:
Observable.range(8, 10).toMultimap(Integer::toHexString).subscribe(System.out::print);
上面的两段程序的输出结果是:
{11=17, a=10, b=11, c=12, d=13, e=14, f=15, 8=8, 9=9, 10=16}
{11=[17], a=[10], b=[11], c=[12], d=[13], e=[14], f=[15], 8=[8], 9=[9], 10=[16]}
上面的两个方法的定义是(多个重载,选择部分):
public final <K> Single<Map<K, T>> toMap(final Function<? super T, ? extends K> keySelector)
public final <K> Single<Map<K, Collection<T>>> toMultimap(Function<? super T, ? extends K> keySelector)
3.toFlowable
该方法用于将一个Observable转换成Flowable类型,下面是该方法的定义,显然这个方法使用了策略模式,这里面涉及背压相关的内容,我们后续再详细介绍。
public final Flowable toFlowable(BackpressureStrategy strategy)
4.to
相比于上面的方法,to
方法的限制更加得宽泛,你可以将指定的Observable转换成任意你想要的类型(如果你可以做到的话),下面是一个示例代码,用来将指定的整数序列转换成另一个整数类型的Observable,只不过这里的每个数据项都是原来的列表中的数据总数的值:
Observable.range(1, 5).to(Observable::count).subscribe(System.out::println);
下面是该方法的定义:
public final <R> R to(Function<? super Observable<T>, R> converter)
2.2 线程控制
之前有提到过RxJava的线程控制是通过subscribeOn
和observeOn
两个方法来完成的。 这里我们梳理一下RxJava提供的几种线程调度器以及RxAndroid为Android提供的调度器的使用场景和区别等。
Schedulers.io()
:代表适用于io操作的调度器,增长或缩减来自适应的线程池,通常用于网络、读写文件等io密集型的操作。重点需要注意的是线程池是无限制的,大量的I/O调度操作将创建许多个线程并占用内存。Schedulers.computation()
:计算工作默认的调度器,代表CPU计算密集型的操作,与I/O操作无关。它也是许多RxJava方法,比如buffer()
,debounce()
,delay()
,interval()
,sample()
,skip()
,的默认调度器。Schedulers.newThread()
:代表一个常规的新线程。Schedulers.immediate()
:这个调度器允许你立即在当前线程执行你指定的工作。它是timeout()
,timeInterval()
以及timestamp()
方法默认的调度器。Schedulers.trampoline()
:当我们想在当前线程执行一个任务时,并不是立即,我们可以用trampoline()
将它入队。这个调度器将会处理它的队列并且按序运行队列中每一个任务。它是repeat()
和retry()
方法默认的调度器。
以及RxAndroid提供的线程调度器:
AndroidSchedulers.mainThread()
用来指代Android的主线程
最后
**要想成为高级安卓工程师,必须掌握许多基础的知识。**在工作中,这些原理可以极大的帮助我们理解技术,在面试中,更是可以帮助我们应对大厂面试官的刁难。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
Schedulers.immediate()
:这个调度器允许你立即在当前线程执行你指定的工作。它是timeout()
,timeInterval()
以及timestamp()
方法默认的调度器。
5. Schedulers.trampoline()
:当我们想在当前线程执行一个任务时,并不是立即,我们可以用trampoline()
将它入队。这个调度器将会处理它的队列并且按序运行队列中每一个任务。它是repeat()
和retry()
方法默认的调度器。
以及RxAndroid提供的线程调度器:
AndroidSchedulers.mainThread()
用来指代Android的主线程
最后
**要想成为高级安卓工程师,必须掌握许多基础的知识。**在工作中,这些原理可以极大的帮助我们理解技术,在面试中,更是可以帮助我们应对大厂面试官的刁难。
[外链图片转存中…(img-tqZkFvlo-1715749087217)]
[外链图片转存中…(img-PFKZrnj1-1715749087219)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!