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

这里我们给出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方法,它可以将原始序列的每个数据项转换成一个集合类型:

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]}

上面的两个方法的定义是(多个重载,选择部分):

  1. public final <K> Single<Map<K, T>> toMap(final Function<? super T, ? extends K> keySelector)
  2. 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的线程控制是通过subscribeOnobserveOn两个方法来完成的。 这里我们梳理一下RxJava提供的几种线程调度器以及RxAndroid为Android提供的调度器的使用场景和区别等。

  1. Schedulers.io():代表适用于io操作的调度器,增长或缩减来自适应的线程池,通常用于网络、读写文件等io密集型的操作。重点需要注意的是线程池是无限制的,大量的I/O调度操作将创建许多个线程并占用内存。
  2. Schedulers.computation():计算工作默认的调度器,代表CPU计算密集型的操作,与I/O操作无关。它也是许多RxJava方法,比如buffer(),debounce(),delay(),interval(),sample(),skip(),的默认调度器。
  3. Schedulers.newThread():代表一个常规的新线程。
  4. Schedulers.immediate():这个调度器允许你立即在当前线程执行你指定的工作。它是timeout(),timeInterval()以及timestamp()方法默认的调度器。
  5. Schedulers.trampoline():当我们想在当前线程执行一个任务时,并不是立即,我们可以用trampoline()将它入队。这个调度器将会处理它的队列并且按序运行队列中每一个任务。它是repeat()retry()方法默认的调度器。
    以及RxAndroid提供的线程调度器:
    AndroidSchedulers.mainThread()用来指代Android的主线程

2.3 总结

上面的这些操作也基本适用于FlowableSingleCompletableMaybe
我们花费了很多的时间和精力来梳理了这些方法,按照上面的内容,使用RxJava实现一些基本的或者高级的操作都不是什么问题。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

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

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

img

img

img

img

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

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

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

结尾

最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。

首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)

泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

接下来是资料清单:(敲黑板!!!


1.数据结构和算法

2.设计模式

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

[外链图片转存中…(img-jDVVxv2X-1711872134089)]

接下来是资料清单:(敲黑板!!!


1.数据结构和算法

[外链图片转存中…(img-jvOUbby0-1711872134089)]

2.设计模式

[外链图片转存中…(img-HDmr4tE6-1711872134089)]

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

[外链图片转存中…(img-VAwkgNxI-1711872134090)]

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

[外链图片转存中…(img-xdSlfJPF-1711872134090)]

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

[外链图片转存中…(img-zQnHDRcB-1711872134090)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值