RxJava 响应式编程 _ 奉上一篇全面的 RxJava2 方法总结(1)

  1. public final <U, V> Observable<V> flatMapIterable(Function<? super T, ? extends Iterable<? extends U>> mapper, BiFunction<? super T, ? super U, ? extends V> resultSelector)

4.buffer

该方法用于将整个流进行分组。以下面的程序为例,我们会先生成一个7个整数构成的流,然后使用buffer之后,这些整数会被3个作为一组进行输出,所以当我们订阅了buffer转换之后的Observable之后得到的是一个列表构成的OBservable

Observable.range(1, 7).buffer(3)
.subscribe(integers -> System.out.println(Arrays.toString(integers.toArray())));

下面是这个方法及其重载方法的定义,它的重载方法太多,这里我们只给出其中的两个,其他的可以参考RxJava的源码。这里的buffer应该理解为一个缓冲区,当缓冲区满了或者剩余的数据不够一个缓冲区的时候就将数据发射出去。

  1. public final Observable<List<T>> buffer(int count)
  2. public final Observable<List<T>> buffer(int count, int skip)

5.groupBy

groupBy用于分组元素,它可以被用来根据指定的条件将元素分成若干组。它将得到一个Observable<GroupedObservable<T, M>>类型的Observable。如下面的程序所示,这里我们使用concat方法先将两个Observable拼接成一个Observable,然后对其元素进行分组。这里我们的分组依据是整数的值,这样我们将得到一个Observable<GroupedObservable<Integer, Integer>>类型的Observable。然后,我们再将得到的序列拼接成一个并进行订阅输出:

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 + " "));

以上方法的定义是:

  1. public final Observable<T> scan(BiFunction<T, T, T> accumulator)
  2. public final <R> Observable<R> scan(R initialValue, BiFunction<R, ? super T, R> accumulator)

7.window

windowWindow和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)));

除了对数据包进行分组,我们还可以根据时间来对发射的数据进行分组。该方法有多个重载的版本,这里我们给出其中的比较具有代表性的几个:

  1. public final Observable<Observable<T>> window(long count)
  2. public final Observable<Observable<T>> window(long timespan, long timeskip, TimeUnit unit)
  3. public final <B> Observable<Observable<T>> window(ObservableSource<B> boundary)
  4. 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);

下面是该方法的定义:

  1. 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及其相关的方法的定义,它们的使用相似。注意一下这里的返回类型:

  1. public final Maybe<T> elementAt(long index)
  2. public final Single<T> elementAt(long index, T defaultItem)
  3. public final Single<T> elementAtOrError(long index)

除了获取指定索引的元素的方法之外,RxJava中还有可以用来直接获取第一个和最后一个元素的方法,这里我们直接给出方法的定义:

  1. public final Maybe<T> firstElement()
  2. public final Single<T> first(T defaultItem)
  3. public final Single<T> firstOrError()
  4. public final Maybe<T> lastElement()
  5. public final Single<T> last(T defaultItem)
  6. 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);

该方法也有几个功能相似的方法,这里给出它们的定义如下:

  1. public final Observable<T> distinct()
  2. public final <K> Observable<T> distinct(Function<? super T, K> keySelector)
  3. public final <K> Observable<T> distinct(Function<? super T, K> keySelector, Callable<? extends Collection<? super K>> collectionSupplier)
  4. public final Observable<T> distinctUntilChanged()
  5. public final <K> Observable<T> distinctUntilChanged(Function<? super T, K> keySelector)
  6. 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);

与上面的这些方法类似的还有一些,这里我们不再一一列举。因为这些方法的重载方法比较多,下面我们给出其中的具有代表性的一部分:

  1. public final Observable<T> skip(long count)
  2. public final Observable<T> skip(long time, TimeUnit unit, Scheduler scheduler)
  3. public final Observable<T> skipLast(int count)
  4. public final Observable<T> skipLast(long time, TimeUnit unit, Scheduler scheduler, boolean delayError, int bufferSize)
  5. public final <U> Observable<T> skipUntil(ObservableSource<U> other)
  6. 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);

下面是以上相关的方法的定义,同样的,我们只选择其中比较有代表性的几个:

  1. public final Observable<T> take(long count)
  2. public final Observable<T> takeLast(long count, long time, TimeUnit unit, Scheduler scheduler, boolean delayError, int bufferSize)
  3. public final <U> Observable<T> takeUntil(ObservableSource<U> other)
  4. public final Observable<T> takeUntil(Predicate<? super T> stopPredicate)
  5. public final Observable<T> takeWhile(Predicate<? super T> predicate)

6.ignoreElements

该方法用来过滤所有源Observable产生的结果,只会把Observable的onComplete和onError事件通知给订阅者。下面是该方法的定义:

  1. 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 + " "));

其他的几个方法的功能大致列举如下:

  1. throttleFirst只会发射指定的Observable在指定的事件范围内发射出来的第一个数据;
  2. throttleLast只会发射指定的Observable在指定的事件范围内发射出来的最后一个数据;
  3. throttleLatest用来发射距离指定的时间分片最近的那个数据;
  4. throttleWithTimeout仅在过了一段指定的时间还没发射数据时才发射一个数据,如果在一个时间片达到之前,发射的数据之后又紧跟着发射了一个数据,那么这个时间片之内之前发射的数据会被丢掉,该方法底层是使用debounce方法实现的。如果数据发射的频率总是快过这里的timeout参数指定的时间,那么将不会再发射出数据来。

下面是这些方法及其重载方法的定义(选择其中一部分):

  1. public final Observable<T> throttleFirst(long skipDuration, TimeUnit unit, Scheduler scheduler)
  2. public final Observable<T> throttleLast(long intervalDuration, TimeUnit unit, Scheduler scheduler)
  3. public final Observable<T> throttleLatest(long timeout, TimeUnit unit, Scheduler scheduler, boolean emitLast)
  4. 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及其几个功能相关的方法的定义:

  1. public final Observable<T> startWith(Iterable<? extends T> items)
  2. public final Observable<T> startWith(ObservableSource<? extends T> other)
  3. public final Observable<T> startWith(T item)
  4. 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的源代码:

  1. public static <T> Observable<T> merge(Iterable<? extends ObservableSource<? extends T>> sources)
  2. public static <T> Observable<T> mergeArray(ObservableSource<? extends T>... sources)
  3. public static <T> Observable<T> mergeDelayError(Iterable<? extends ObservableSource<? extends T>> sources)
  4. 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);

下面是该方法的定义,鉴于该方法及其重载方法太多,这里我们选择几个比较有代表性的说明:

  1. public static <T> Observable<T> concat(Iterable<? extends ObservableSource<? extends T>> sources)
  2. public static <T> Observable<T> concatDelayError(Iterable<? extends ObservableSource<? extends T>> sources)
  3. public static <T> Observable<T> concatArray(ObservableSource<? extends T>... sources)
  4. public static <T> Observable<T> concatArrayDelayError(ObservableSource<? extends T>... sources)
  5. public static <T> Observable<T> concatEager(ObservableSource<? extends ObservableSource<? extends T>> sources)
  6. 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方法有多个重载的版本,同时也有功能近似的方法,这里我们挑选有代表性的几个进行说明:

  1. public static <T, R> Observable<R> zip(Iterable<? extends ObservableSource<? extends T>> sources, Function<? super Object[], ? extends R> zipper)
  2. ublic static <T, R> Observable<R> zipArray(Function<? super Object[], ? extends R> zipper, boolean delayError, int bufferSize, ObservableSource... sources)
  3. 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方法也有几个重载的方法,可以供我们用来指定触发的线程等信息,这里给出其中的两个,其他的可以参考源码和文档:

  1. public final Observable<T> delay(long delay, TimeUnit unit)
  2. public final Observable<T> delay(long delay, TimeUnit unit, Scheduler scheduler)

2.do系列

RxJava中还有一系列的方法可以供我们使用,它们共同的特点是都是以do开头,下面我们列举一下这些方法并简要说明一下它们各自的用途:

  1. public final Observable<T> doAfterNext(Consumer<? super T> onAfterNext),会在onNext方法之后触发;
  2. public final Observable<T> doAfterTerminate(Action onFinally),会在Observable终止之后触发;
  3. public final Observable<T> doFinally(Action onFinally),当onComplete或者onError的时候触发;
  4. public final Observable<T> doOnDispose(Action onDispose),当被dispose的时候触发;
  5. public final Observable<T> doOnComplete(Action onComplete),当complete的时候触发;
  6. public final Observable<T> doOnEach(final Observer<? super T> observer),当每个onNext调用的时候触发;
  7. public final Observable<T> doOnError(Consumer<? super Throwable> onError),当调用onError的时候触发;
  8. public final Observable<T> doOnLifecycle(final Consumer<? super Disposable> onSubscribe, final Action onDispose)
  9. public final Observable<T> doOnNext(Consumer<? super T> onNext),,会在onNext的时候触发;
  10. public final Observable<T> doOnSubscribe(Consumer<? super Disposable> onSubscribe),会在订阅的时候触发;
  11. 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,用来对错误信息做统一的处理,常用的两个是catchretry

1.catch

catch操作会拦截原始的Observable的onError通知,将它替换为其他数据项或者数据序列,让产生的Observable能够正常终止或者根本不终止。在RxJava中该操作有3终类型:

  1. onErrorReturn:这种操作会在onError触发的时候返回一个特殊的项替换错误,并调用观察者的onCompleted方法,而不会将错误传递给观察者;
  2. onErrorResumeNext:会在onError触发的时候发射备用的数据项给观察者;
  3. onExceptionResumeNext:如果onError触发的时候onError收到的Throwable不是Exception,它会将错误传递给观察者的onError方法,不会使用备用的Observable。

下面是onErrorReturnonErrorResumeNext的程序示例,这里第一段代码会在出现错误的时候输出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方法。也就是说,下面这段代码会触发onNext3次,触发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有几个重载的方法和功能相近的方法,下面是这些方法的定义(选取部分):

  1. public final Observable<T> retry():会进行无限次地重试;
  2. public final Observable<T> retry(BiPredicate<? super Integer, ? super Throwable> predicate)
  3. public final Observable<T> retry(long times):指定重试次数;
  4. public final Observable<T> retry(long times, Predicate<? super Throwable> predicate)
  5. public final Observable<T> retryUntil(final BooleanSupplier stop)
  6. public final Observable<T> retryWhen(Function<? super Observable<Throwable>, ? extends ObservableSource<?>> handler)
2.1.7 条件操作符和布尔操作符

1.all & any

  1. all用来判断指定的数据项是否全部满足指定的要求,这里的“要求”可以使用一个函数来指定;
  2. 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

以下是该方法的定义:

  1. public final Single<Boolean> all(Predicate<? super T> predicate)
  2. 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

以下是这两个方法的定义:

  1. public final Single<Boolean> isEmpty()
  2. 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)

该方法及其功能近似的方法的定义,这里前两个是静态的方法,第二个属于实例方法:

  1. public static <T> Observable<T> amb(Iterable<? extends ObservableSource<? extends T>> sources)
  2. public static <T> Observable<T> ambArray(ObservableSource<? extends T>... sources)
  3. 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);

下面是该方法的定义:

  1. public final Observable<T> defaultIfEmpty(T defaultItem)
2.1.8 转换操作符

1.toList & toSortedList

toListtoSortedList用于将序列转换成列表,后者相对于前者增加了排序的功能:

Observable.range(1, 5).toList().subscribe(System.out::println);
Observable.range(1, 5).toSortedList(Comparator.comparingInt(o -> -o)).subscribe(System.out::println);

下面是它们的定义,它们有多个重载版本,这里选择其中的两个进行说明:

  1. public final Single<List<T>> toList()
  2. 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方法,它可以将原始序列的每个数据项转换成一个集合类型:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

资源分享

一线互联网面试专题

379页的Android进阶知识大全

379页的Android进阶知识大全

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2020年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

资源分享

[外链图片转存中…(img-yLpehpVi-1712671863868)]

[外链图片转存中…(img-94ip4umt-1712671863868)]

[外链图片转存中…(img-au40NEJM-1712671863869)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2020年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值