前言:
本篇基于Map操作符,阅读该篇请确保,你已经了解过它:RxJava2 转换操作符之Map()方法。一如既往,干大事的人都是直接先上图,再解释的。(建议用PC端阅读,图片可以放大。排版,也有利于阅读)
二、FlatMap的操作流程图解
先来跟你讲讲,这图咋看。这上面的线,表示原始数据。下面的线,表示转换后的数据。中间的块,代表转换过程。两条线的结尾有一小条竖线,表示结束。箭头的方向,从左到右,表示运行过程,是从左到右跑的。
1、(Question-1)你可能会奇怪,为毛从左到右数,第5个会是绿色的,不应该也是蓝色的吗?
这个,得分成多步解释,请耐心一下:
2、(Question-2)不是说,一个一个执行吗?那怎么会出现交叉(就是两蓝中插了个一绿的)呢?
因为,不同的二级任务的执行时间是不同的。比方说:第一个绿色棱形的任务是计算:1+1。第二个绿色的棱形任务是:从服务器获取用户信息和系统配置。第一个蓝色棱形的任务是计算:2+2。第二个蓝色的棱形任务是:做特别复杂耗时的操作,比第二个绿色的棱形的任务执行的还要久。那么,从例子可以看出。第一个绿色棱形肯定是先完成。但第二个绿色棱形的任务,执行的时间肯定比,第一个蓝色棱形的任务还要久。所以,就会出现,先出现第一个蓝色棱形,后再出现第二个绿色的棱形。。。
三、少跟我说理论!上代码,要复制就能跑,一眼就看得懂的。
答:… 先喝点加多宝,我这就上代码。
public static void actionFlatMap() {
List<Integer> list = Arrays.asList(1, 2, 3);
// 这里用了lambda。
Observable.fromIterable(list)
.flatMap(integer -> {
log("开始执行,第" + integer + "圆球的任务" + getThreadName());
return getObservable(integer);
}).subscribe(s -> log("已完成" + s + getThreadName()));
}
public static Observable<String> getObservable(final int integer) {
return Observable.create((ObservableOnSubscribe<String>) emitter -> {
emitter.onNext("第" + integer + "圆球的第1个棱形任务");
if(integer != 1) {
// 第2和第3个圆球的第二个任务延时。
Thread.sleep(5 * 1000);
}
emitter.onNext("第" + integer + "圆球的第2个棱形任务");
emitter.onComplete();
}).subscribeOn(Schedulers.newThread());
}
// 返回当前的线程名
public static String getThreadName() {
return " | ThreadName=" + Thread.currentThread().getName();
}
private static void log(String log) {
Log.d("FlatMap", log);
}
这是运行结果:
D/TransformingOperations: 开始执行,第1圆球的任务 | ThreadName=main
D/TransformingOperations: 开始执行,第2圆球的任务 | ThreadName=main
D/TransformingOperations: 开始执行,第3圆球的任务 | ThreadName=main
D/TransformingOperations: 已完成第2圆球的第1个棱形任务 | ThreadName=RxNewThreadScheduler-2
D/TransformingOperations: 已完成第3圆球的第1个棱形任务 | ThreadName=RxNewThreadScheduler-3
D/TransformingOperations: 已完成第1圆球的第1个棱形任务 | ThreadName=RxNewThreadScheduler-3
D/TransformingOperations: 已完成第1圆球的第2个棱形任务 | ThreadName=RxNewThreadScheduler-3
D/TransformingOperations: 已完成第2圆球的第2个棱形任务 | ThreadName=RxNewThreadScheduler-2
D/TransformingOperations: 已完成第3圆球的第2个棱形任务 | ThreadName=RxNewThreadScheduler-2
从打印的结果可以看到,FlatMap,首先是从1到3,即从左到右,执行任务的。其中,1、2、3又各自包含2个子任务。虽然是从1到3开始执行。但是,很明显,未必就是1先执行完毕。反而是,2、3的第一个任务先完成,然后,才是1的两个任务完成。然后,才是2、3的两个被延时处理的任务被分别完成。从这个例子,我们得出这样一个结论:FlatMap执行流程是:先将所有一级任务,铺平成所有二级任务。再依照,从左到右到执行次序,执行任务。但是,任务成功的回调,却不是从左到右的。而是,谁先完成谁先回调。简言之,即:执行次序是一定的,完成次序是不确定的。
四、让我们来看看Rx官方是怎么解释的
FlatMap,英文水平有限,如果翻译有不当之处,欢迎各种建议。
1、transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable。
翻译过来就是:由一个Observable发射一组任务,来将它们转成多个Observable。然后,铺平那些Observable到一个Observable里面。听起来是不是很难理解?因为这是硬翻译,来看看软翻译怎么说:
由一个Observable来触发执行,一组转换任务。做法是:先将这些任务以及它们的子任务,提出来。然后,再将这些任务合并到一个Observable里面。最后,由这个Observable,对这些任务进行遍历处理。。。如果还看不懂,没关系,下面还有更详细的解释。
2、The FlatMap operator transforms an Observable by applying a function that you specify to each item emitted by the source Observable, where that function returns an Observable that itself emits items. FlatMap then merges the emissions of these resulting Observables, emitting these merged results as its own sequence.
This method is useful, for example, when you have an Observable that emits a series of items that themselves have Observable members or are in other ways transformable into Observables, so that you can create a new Observable that emits the complete collection of items emitted by the sub-Observables of these items.
Note that FlatMap merges the emissions of these Observables, so that they may interleave.
In several of the language-specific implementations there is also an operator that does not interleave the emissions from the transformed Observables, but instead emits these emissions in strict order, often called ConcatMap or something similar.
FlatMap操作符,转换一个Observable的做法是,对每一个任务,都通过调用SourceObservable的方法来实现转换。这个方法由你实现,具体的转换逻辑,就在这个方法里面处理。并且,该方法会返回一个Observable,这个Observable又可以处理它自己的任务。FlatMap会合并所有的任务,即将一级任务,先全部转成二级任务,再遍历处理。
这个方法是很有用的。比如说,当你的Observable执行一组任务,或处理一组数据的同时。这些任务或数据,又包含有自己的任务,或数据。所以,你可以为每一个任务,创建一个Observable来处理,它们的二级任务。
注意:因为FlatMap会合并所有的任务。所以,它们可能会有交叉现象。。这句话的意思是,因为将所有一级任务的二级任务都合并成一条线。然后,遍历执行。这样,有的任务,可能会因耗时而慢回调。从而导致,先执行,后回调的现象。
最后一段,简而言之:如果,你既想实现这个功能,又不想出现交叉。即每一个任务,都会等前一个任务,执行完,再回调。可以用ConcatMap操作符。下篇,我们会讲:ConcatMap操作符和FlatMapIterable操作符。
到此为止,FlatMap操作符的讲解,已经全部说完了。如果,你还有什么疑惑不解,可以在评论区指出。
附上可以跑的代码(0 warnings, 0 errors):
https://github.com/SuperBeagleDog/OkMVP
注意:
这个库里面有很多东西,RxJava2的转换操作符部分的demo位于:com.lyf.okmvp.demo.rxjava2包下的TransformingOperations类里面。
用法:
1、直接在com.lyf.okmvp.ui包下的MainActivity类里的onCreate()方法里面,直接调用:
TransformingOperations.actionFlatMap(); // 静态方法。
2、也可以复制TransformingOperations类到你的项目里,去为所欲为。但前提时,你得有配置过RxJava和RxAndroid。配置方法,自行百度,或参考我的OkMVP库。