一. 前语
我尽力做到让这篇文章成为全网最通俗易懂,最细致全面的RxJava运用教程。
在这篇文章中,你将会看到全网最细致的map与flatMap解说 。。。
前面的文章中,我现已把RxJava是什么以及它的好处,讲的十分清楚了。咱们现在是不是热情高涨,刻不容缓想要开端实操了?又或许平时尽管其实经常用RxJava,但并不知道它的“规范”运用
所谓“规范”运用,便是一个模板,包含RxJava的绝大部分运用方向
这篇文章,便是来处理这些问题的。
学完这篇文章,你将对RxJava的功用有更加直观的了解,对RxJava的运用方向熟记在心。话不多说,咱们直接开端吧!
本篇文章有数万字,或许需求花费你1h的时刻来进行研究,不过,我向你确保,假如你之前没有触摸过RxJava的运用,又或许对它的运用有些模糊,那么,这1h是肯定值得你花费的。
二. 一个规范运用事例
所谓规范运用事例,便是一个包含了这个框架根本功用的代码。这个代码会十分简洁,结构十分明晰,让你可以时不时地拿来翻阅,看完后就觉得思路明晰。
需求阐明的是,这儿只讲了RxJava2的运用事例,并不会触及RxJava1和RxJava3。原因是RxJava1比较长远,一起咱们对RxJava2比RxJava3更了解。
规范事例代码
关于RxJava2,其实是有两套运用办法,一是Observable/Observer,二是Flowable/Subscriber。两者的运用办法总体上相同,没有什么大的不同。咱们先以Observable/Observer这一对儿为基础解说,等理透了这一对的思路后,你天然也就掌握了Flowable/Subscriber的根本运用(两者运用起来很像,所以在本篇文章中,以Observable/Observer为重点来进行解说)。
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
e.onNext(1);
e.onNext(10);
e.onComplete();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.delay(5, TimeUnit.SECONDS)
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer integer) throws Exception {
return integer + 1;
}
})
.subscribe(new Observer<Integer>(){
Disposable mDisposable;
@Override
public void onSubscribe(Disposable d) {
mDisposable = d;
}
@Override
public void onNext(Integer value) {
if (value == 10) {
mDisposable.dispose();
}
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
这便是一个十分十分规范的RxJava的运用代码,心急的人或许看到有这么一长串,心里现已烦躁了,我知道你很急,但你先别急,请你仔细的一行一行看完,看完后,记不住,不了解都没有联系,由于接下来我将会为你区分结构,你一看就会茅塞顿开。但前提是你必须一行一行看完事例代码,这样才能到达最佳效果。
看到这儿,你是否感觉明晰了许多?没错,RxJava的结构便是这么明晰。假如你觉得还不够简洁明了的话,可以反复看看这张图,然后来看我下面的解说。
关于RxJava的运用,我区分成了以下几个部分。
- 创立被观察者并界说发送事情的办法
- 创立观察者
- 数据转化
- 线程切换
- 免除订阅
- 其他功用
这些点,都与上图逐个对应。这样区分,逻辑是十分明晰的。即先知道RxJava两大人物怎样运用,再知道中途数据流动过程中的一些处理办法,最终再知道免除的办法,齐活儿!
下面,我就一个一个地来介绍~
运用点1:创立被观察者并界说发送事情的办法
咱们现已知道,RxJava有个十分著名的当地,便是观察者办法。观察者办法的两大人物,便是被观察者和观察者。在RxJava这儿,也不破例。咱们需求知道RxJava创立被观察者的几种办法。尽管办法有许多,但本质都是相同的,便是创立Observable
。所以咱们也不用惧怕,一起,RxJava在创立被观察者目标时,往往也界说了发送事情的办法,所以这两个可以组合为一个点解说。
下面我将从简略的办法开端,避免把咱们弄模糊。
通用的创立被观察者的办法
十分简略,直接看代码
Observable.create(new ObservableOnSubscribe<T>() {
@Override
public void subscribe(ObservableEmitter<T> e) throws Exception {
// 界说发送事情的办法
}
})
这种办法不只简略,而且也是十分常用的一种办法。
在上面的代码中,咱们经过调用Observable的create
办法来创立被观察者。在create
办法里边,需求传入一个ObservableOnSubscribe<T>
类型的参数,其间<T>
表示要发送的数据的类型,比方Integer等。而且也完成了subscribe
办法,此办法的效果便是界说事情发送的办法。要想发送事情,就调用e.onNext()
办法即可。比方:
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
e.onNext(1);
e.onNext(10);
e.onComplete();
}
})
这样之后,咱们就创立了一个被观察者目标。这种办法也是十分规范的一种办法,结构明晰,也便利自界说一些完成。接下来,我将说一些不是那么主流的创立办法,但在实际的项目中,却时有用到。
其他的创立被观察者的办法
just
比方
Observable.just(1, 2, 3)
可以顺次发射参数,发送完后会主动调用onComplete()
办法终止。
在这儿便是会主动发射整数1、2、3,并在发射完成后主动调用onComplete()
办法。
其实这种办法,等价于
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
e.onNext(1);
e.onNext(2);
e.onNext(3);
e.onComplete();
}
})
只不过它写起来的确要简略一些,一切有时可以直接运用 just。
运用 just 时,需求留意一点,即它并不是随意塞参数的。最多只能承受10个参数。结论来自源码:
所以,它只适用于参数较少的状况。假如参数的确比较多,那就可以运用下一种办法
fromArray
比方
Observable.fromArray(new int[]{1, 2, 3});
这样就没有像 just 那样严厉的大小约束了,它会顺次发送数组中的每个元素。
其他的
除了 just 和 fromArray,其实还有许多其他的,在这儿就不逐个介绍了,意图都是相同的,便是创立被观察者,一起界说发送事情的办法。至于具体有哪些,你可以点进Observable,看它里边都有哪些静态办法,这些根本就都是了
运用点2:创立观察者的办法
通用的创立观察者的办法
其实在上面的代码示例中也现已给出了,
new Observer<Integer>(){
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Integer value) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
}
Observer其实是一个接口,咱们经过new Observer来创立它的完成类,一共需求完成四个办法,onSubscribe
、onNext
、onError
和onComplete
。
(1)onSubscribe
是产生订阅的回调,即调用subscribe
办法,Observer订阅Observable时,首要会调用onSubscribe
办法。咱们可以在这儿做一些初始化相关的工作。
(2)onNext
是Observable来调用的。一般是真实的发送事情。比方上面比方中的,发送一个Integer类型数据。Observer的onNext
办法可以接纳到这个数字,对其进行一些处理。
(3)onError
,见名知意,便是产生错误时调用。
(4)onComplete
,见名知意,便是事情流结束时调用。
这便是一个十分规范的观察者的创立办法。下面咱们来看一点其他的创立办法
其他的创立观察者的办法
当咱们在Observable类中查找subscribe办法时,可以看到它不只可以承受Observer类型的参数(即上面讲到的规范的创立办法),还可以承受Consumer类型的参数。如图
所以Consumer便是咱们要讲到的第二种创立观察者的办法。
new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
}
}
可以看到这儿只要一个完成办法,即accept
。你可以简略的了解为,这个accept
便是指代的onSubscribe、onNext、onError
。具体是指代哪一个,需求看咱们调用subscribe
的办法。从上面那张图中咱们可以看出subscribe
可以接纳多个Consumer,也可以接纳一个Consumer。accept
到底指代的哪一个办法,需求从subscribe
办法界说上找答案。
当只要一个Consumer参数时,accept代表的是onNext
从参数名可以看出来
当有两个Consumer参数时,第一个accept代表onNext,第二个accept代表onError
当有三个Consumer参数时,第一个代表onNext,第二个代表onError,第三个代表onSubscribe
值得一提的是,onComplete办法并无法经过Consumer代替,而是Action类型。
彩蛋:Consumer和Observer是什么联系?
咱们这个小节在讲创立观察者的几种办法。首要讲到了创立Observer的完成类,然后讲到了Consumer。阐明这两个都可以作为“观察者”,但这两个有什么差异呢?其实这儿很有意思,咱们仍是从源码中找答案。
当咱们调用subscribe办法,传入Consumer参数的时分,实际内部会调用
subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe
根据在这儿
咱们看看这个办法内部做了什么事情
首要进行了各种判空,然后,把Consumer作为结构参数,创立了LambdaObserver目标!然后调用了subscribe
办法,把LambdaObserver目标作为参数传进去了!而subscribe
办法,便是咱们创立Observer的时分调用的办法
在这儿咱们可以得出结论:Consumer最终仍是会被封装为Observer!
也即,不管是Observer,仍是Consumer,终究都会成为Observer,而且调用到
subscribe(Observer<? super T> observer)
办法完成订阅,只不过Consumer内部作了一层转化,被包成了LambdaObserver(其实LambdaObserver也是一个完成了Observer接口的完成类)
以上,就把RxJava创立观察者和被观察者的办法解说完成了。下面咱们来看在数据流动的过程中,RxJava可以做哪些事情。
运用点3:数据转化的办法
咱们在上面的规范事例中,写到了一个数据转化的办法,便是map操作符,所以咱们首要来看看这个map操作符的效果
map操作符
咱们在java中,也用到过 map,比方 HashMap。咱们都知道,它是经过键值对存储,一个 key 对应一个 value。或许咱们也可以这样了解,即一个 key 可以转化成一个 value。key 和 value 的类型或许不同,但 value 可以由 key 转化而来,即key可以转化成一个value。
了解了这一点,map操作符也就了解了。它的功用也是完成转化,也是将一个值转化为别的一个值,两个值类型可以不同,但对应着一个转化联系。比方下面的比方
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer integer) throws Exception {
return integer + 1;
}
})
里边的apply
办法,你可以了解为便是界说转化规则的办法。参数类型是Integer,办法也返回了Integer类型。也便是说它将一个Integer类型的数据转化成了一个Integer类型的数据,具体的转化规则是将输入的Integer数据加1。输入的Integer(即办法参数)就类似于 map 中的 key,输出的Integer就类似于 map 中的 value。
map办法里边需求传入一个Function类型的参数,在Function类中,有两个泛型,前一个代表输入的数据类型,后一个代表输出的数据类型。比方
红圈表示Integer类型,蓝圈表示String类型。整个数据的转化,是由Integer类型转为了String类型。
关于map的运用,你是否现已很清楚了呢?下面咱们来看一个和map很像的操作符:flatMap
flatMap操作符
关于flatMap,网上的文章众说纷纭,我看了许多,感觉都很懵,所以在这儿,我建议咱们遗忘之前对flatMap的印象,从0开端跟着我的思路掌握它,你会发现十分的明晰。
本小节内容比较多,但我向你确保,你只需求花10分钟,仔细了解透彻举例内容,你就会彻底搞懂flatMap的运用。我确保,这是你没有见过的视点,而且这个视点十分通俗易懂。但前提是你需求花时刻看完。
咱们首要需求拿它和map操作符进行一个比照。比照的办法,就看源码界说吧:
map的源码界说
咱们需求留意的点有三个部分,我都用红框的办法标注出来了。
第一是注释,第二是map办法的返回值类型,第三是map办法的Function参数中泛型的界说。让咱们来逐个解读一下。
首要注释部分,写到参数是一个函数,运用于ObservableSource宣布的每个项。返回值是一个Observable目标,它从源ObservableSource中宣布项目,并经过指定的函数进行转化。我翻译的比较僵硬,但也大概可以看懂。
然后是map的返回值类型,是一个Observable类型
然后是Function的泛型。这儿的泛型,除了要求第一个泛型数据,要和上游传下来的数据类型相同外(本文前面的内容有解说),根本没有明晰的约束。
好,接下来,让咱们看一下 flatMap 的源码界说
flatMap的源码界说
相同的,咱们依然需求重视这三个部分。
相同的,我先来解读一下注释:参数是一个函数,当运用于源ObservableSource宣布的项时,返回一个ObservableSource(这儿和map就不同了)。返回值是一个Observable目标,它宣布将转化函数运用于源ObservableSource宣布的每个项的成果,并兼并从该转化获得的ObservableSource的成果(这儿的兼并,你不用故意去了解,先忽视这个词,否则容易跑偏)
然后是 flatMap 的返回值类型,依然是一个Observable类型
然后是Function的泛型,这儿的泛型,除了有和 map 相同的约束外,还多了一个约束,即? extends ObservableSource<? extends R>
。即要求返回值,比方是ObservableSource类型的数据。
看到这,你是不是有点懵?懵就对了,但咱们要清楚咱们懵的点,其实咱们懵的点在于,对 map 和 flatMap 的运用差异,不清楚。但咱们对 map 和 flatMap 自身的界说,现已很清楚了,便是上文的罗列。在这儿,我再总结一下:flatMap的Function参数,内部的apply
办法,要求返回一个ObservableSource类型的数据,而 map 没有这样的要求,仅此而已,其他的根本彻底相同。
为了验证此观念,咱们可以写两个demo看下履行效果。首要,来看 flatMap 的比方:
Observable.just("1", "2", "3")
.flatMap(new Function<String, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(String s) throws Exception {
return Observable.just(s); // 留意这儿的返回值
}
}).subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.v("tag", "onSubscribe");
}
@Override
public void onNext(String s) {
Log.v("tag", "onNext s:" + s);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
Log.v("tag", "onComplete");
}
});
然后,再看个 map 的比方
Observable.just("1", "2", "3")
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
return s; // 留意这儿的返回值
}
}).subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.v("tag", "onSubscribe");
}
@Override
public void onNext(String s) {
Log.v("tag", "onNext s:" + s);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
Log.v("tag", "onComplete");
}
});
当运转后,你会发现,两者的成果彻底相同!都是:
onSubscribe
onNext s:1
onNext s:2
onNext s:3
onComplete
所以,map 和 flatMap 界说上的差异,便是Function要求的返回值不同。那这两个的运用场景,又有什么差异呢?别急,马上就要阅历大彻大悟的感觉了!
首要,咱们看这样一个运用场景,有一群人在排队,每个人,身上都带着一些生果。在部队的周围,有一位大佬,名字叫Observer,Observer需求得到一切人的生果,并逐个显示出来。一起还存在一个检查员的人物,它的效果便是得到每个人身上的生果,而且发送给Observer。至于怎样得到,怎样发送,便是 map 和 flatMap 的差异。我再强调一遍,Observer的要求:得到每一个人的生果,而且逐个显示出来。
用map来完成这样的功用,代码应该这样写:
Observable.fromIterable(people)
.map(new Function<Man, List<String>>() {
@Override
public List<String> apply(Man source) throws Exception {
return source.fruits;
}
})
.subscribe(new Observer<List<String>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(List<String> s) {
for(String str: s) {
Log.v("tag", "onNext s:" + str);
}
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
}
);
people
便是人的集合,也便是比方中的部队,source
目标便是每一个人,它的fruits
属性,便是这个人身上的生果。从这个比方中,可以看到,map 把每一个人身上的生果都一次性悉数拿到,而且全体悉数丢给Observer。
假如用 flatMap 来完成这样的功用呢?
Observable.fromIterable(people)
.flatMap(new Function<Man, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(Man student) throws Exception {
return Observable.fromIterable(student.fruits);
}
})
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String s) {
Log.v("tag", "onNext s:" + s);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
}
);
flatMap 也是一次性把这个人身上的生果悉数拿到,但是它和 map 有一个不同,即它把生果拿到之后,会放到一个生果篮子里,这个生果篮子里包含了这个人身上一切的生果,然后,再顺次地,一个一个地,递给Observer。
咱们请仔细看下上面的两段代码,再持续往后读。
好的,读到这儿,我相信你心里现已大体知道两者的差异了,我在这儿,再总结一下。接着刚刚的比方讲,map 和 flatMap,就类似这样的差异:
检查员map,检查一个人,就把他身上的一切生果直接丢给Observer,Observer一次性悉数接住。
检查员flatMap,检查每个人时,先把他身上的生果,放到一个篮子里,然后再把篮子里的一切生果,顺次给Observer,Observer一个一个地接住,等接完了,检查员再检查下一个人。
这两种办法尽管能完成相同的终究效果,但阅历的过程不同。不同点在于,有没有把生果,放到一个篮子里的操作,一起将生果丢给Observer时,是一次性悉数丢给他,仍是一个一个地给他。
怎样样?是不是对两者的功用差异,现已十分明晰了?在这儿,其实咱们也可以发现一个点,便是 map 和 flatMap 并没有网上说的那么玄幻的不同,其实 flatMap 能完成的效果,map 根本也能完成(比方上面的比方,履行成果是相同的),仅仅两者侧重点不同。
至此,也可以解说下 flatMap 的注释的含义了,我再贴一下注释代码,省的你往上翻了
这儿的所谓兼并(merging),便是比方中提到的,把每一个人身上的一切生果,放到一个篮子里的操作,这便是兼并。结合上面的比方,我相信你能了解透彻 flatMap 的界说了。
读到这儿,我相信你心里现已十分清楚两者功用上的差异了,接下来咱们走到最终一步,即两者的实际运用场景,有何不同?或许说,flatMap 一般用在哪里?
我就直接揭晓答案了:map合适于一对一的数据转化,flatMap合适于一对多的数据转化,而且要求Observer将数据一个一个地进行接纳!
比方,在上面我举的生果比方中,其实便是个一对多的数据转化,即一个人,转化为多个生果,而且Observer更合适一个一个的接纳(谁也不想被人一次性扔过来一堆生果还得全接住吧),这儿其实便是比较合适用 flatMap。再比方,要从数据库中,根据 id,获得 id 对应的许多字段,这儿相当所以一个一对多的转化,即 id,转化为了 id 对应的许多字段,假如下流再要求最好能一个一个字段给我的话,那这儿就十分合适用 flatMap 了
好了,关于 map 和 flatMap 的差异,就讲到这儿了。我相信,经过生果的这个比方,你能印象深刻地记住 map 和 flatMap 的差异,一起,也会知道什么场景用 map,什么场景用 flatMap 了。其实除了 map 和 flatMap ,还有其他的数据转化办法,在这儿就不逐个罗列了,最常用的,其实便是 map。
运用点4:线程切换的办法
在这篇文章中,我其实有提到RxJava可以极其便利的完成线程切换,它完成线程切换,主要是两个办法:subscribeOn和ObserveOn
。
observeOn
用于指定观察者(Observer)履行的线程,而 subscribeOn
用于指定被观察者(Observable)履行的线程
observeOn是指定一次生效一次,而且只对后边的Observer生效,subscribeOn是只要第一次指定生效,后续指定无效。
至于指定什么线程,RxJava给了两个类:Schedulers
和AndroidSchedulers
,这两个类可以便利选择线程,具体有
关于Schedulers:
下面咱们来一个一个地解说,
-
Schedulers.single()
:只要一个工作者线程来履行使命,假如连续多次调用了Schedulers.single(),那么会确保被指定线程的使命,按次序一个接一个地履行,而且他们都在同一个线程里边。适用于需求保持使命履行次序的状况。 -
Schedulers.computation()
:这是一个用于核算密集型使命的线程池。它合适用于履行需求很多核算的操作,比方数据处理或杂乱的数学运算。 -
Schedulers.io()
:这是一个用于 I/O 操作的线程池,适用于履行网络请求、文件读写等耗时的异步使命。 -
Schedulers.trampoline()
:这个调度器会依照行列次序在当时线程上履行使命,适用于需求在当时线程上次序履行的场景。 -
Schedulers.newThread()
:每个使命都在独立的新线程上履行,适用于履行一些比较耗时的异步使命。
关于AndroidSchedulers
咱们只需求重视它的AndroidSchedulers.mainThread()
即可。是用于 Android 运用的特殊调度器,用于在主线程上履行使命,主要用于 UI 更新和界面处理。
让咱们来看一个运用事例吧
Observable.create(new ObservableOnSubscribe<List<String>>() {
@Override
public void subscribe(ObservableEmitter<List<String>> emitter) throws Exception {
Log.v("tag", "subscribe办法,当时线程为: " + Thread.currentThread().getName());
Thread.sleep(1000);
List strList = new ArrayList<String>();
strList.add("1");
strList.add("2");
Log.v("tag", "subscribe办法,拿到数据了,下面进行数据发送");
emitter.onNext(strList);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<String>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(List<String> s) {
Log.v("tag", "onNext办法,当时线程为: " + Thread.currentThread().getName());
Log.v("tag", "onNext办法,收到的数据为: " + s.toString());
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
}
);
履行成果:
看到这儿,你也可以发现,运用RxJava,的确可以完成极其便利的线程转化,即一行代码切一个线程,而且还可以确保履行的先后次序。这给咱们带来了极大的便利,尤其是那种先获取网络请求,再更新UI的场景,用RxJava来写,几乎太香了!
运用点5:免除订阅的办法
当咱们的Observer,不需求再接纳数据的时分,咱们就可以免除订阅了,免除订阅主要是有两个意图:避免内存泄露,减少不用要的资源消耗。
免除订阅的办法很简略,在最初的规范事例中其实也有,我再拿过来,咱们看一下就会了
触及免除订阅的代码逻辑部分,我都用红框标注出来了。还记得我最初讲过吗?除了Observable/Observer这一对,其实还有Flowable/Subscriber这一对,那么后者是如何免除订阅呢? 且看:
其实这儿,运用上很简略,但便是需求咱们记住:千万别忘了免除订阅!!!
其实Observable/Observer和Flowable/Subscriber的免除订阅的原理是不同的,这儿其实很有意思,等咱们讲到原理课的时分,会为咱们解说。
运用点6:其他功用
除了以上讲到的RxJava的运用外,其实RxJava还有一些其他的瑰宝功用,在这儿,我就不逐个介绍了,只简略举一些比方,供你参阅。
比方它可以完成数据延时发送
它也可以设置超时时刻,假如 Observable 在指定的时刻内没有发射任何数据项,那么它会抛出一个 TimeoutException
,从而可以处理超时状况。
除了这些,其实还有,我就不逐个罗列了。
三. 总结
我经过差不多两万字的内容,向你具体介绍了RxJava的运用,在文章最初,我先给了你一个规范运用事例,然后以此为头绪,顺次介绍RxJava各个方向的运用,其间花了很大篇幅解说关于 map、flatMap 的运用。
假如本篇文章你觉得写的还不错的话,我期望你可以把它当作RxJava的“词典”,每逢忘掉某一个当地怎样写的时分,就可以拿来翻阅。当然,有问题欢迎在评论区交流评论。
后边,咱们就开启RxJava原理的学习了!