记录完了lift,再来看看另外两个经常被混淆的概念——map与flatmap。map的定义很容易理解,可以将它就是lift的“人性化”的延伸,但是flatmap就比较难以理解了,“扁平化map“是个啥?
在说map之前,需要提到的是lift实现有个很容易让初学者出现错觉的地方。那就是新数据类型对老数据类型的转换(R->T),需要把新数据关系映射到老数据上去,这个是比较反人类的。而使用lift解决的办法是用户自己构造一个subscriber
这一点虽然合理,但是仍然比较绕。有没有更加亲民的理解方式?我觉得map做到了,它可以让用户认为,改变的并不是subscriber,而是新构造出了一个observable代理,这就是更便于理解的转换关系。
废话太多,还是看map代码
public final Observable map(Func1 super T, ? extends R> func) {
return lift(new OperatorMap(func));
}
lift中最关键的operator实现,就是这个OperatorMap的实现了。
这里RxJava中为了让新老数据类型的转换能够逆转,做了个关键的设计。
注意这个OperatorMap的实现中泛型接口的定义被反转了,在具体接口实现中完成了通用的反转逻辑
public final class OperatorMap<T, R> implements Operator<R, T> {
final Func1 super T, ? extends R> transformer;
@Override
public Subscriber super T> call(final Subscriber super R> o) {
return new Subscriber(o) {
...
@Override
public void onNext(T t) {
try {
o.onNext(transformer.call(t));
} catch (Throwable e) {
Exceptions.throwOrReport(e, this, t);
}
}
};
}
}
可以看到,operatormap的核心同样也是实现了一个新的subscriber
用伪代码来说明下:
r = transformer.call(t)
通过对Func1接口的实现,我们将T类型对象转换为了R类型对象。从这里开始,T类型事件的每一次触发实际都转换为了一个R类型的触发规则,接下来,我们就可以在观测者subscriber
从这里开始,用户就不需要考虑代理的问题了,只需要考虑如何把observable事件中emit出的数据转换为新的数据类型就可以。
在介绍flatmap的原理之前,首先来看一个场景,如果map将待处理数据类型T转换为一个集合类型List
我很喜欢RxJava 之扔物线《给Android开发者的 RxJava 详解》文章中的例子 ,借用扔物线前辈的这段代码,说明下场景:
/**
* 需要:输出每一个学生选修的课程,对method12的简化
* 嵌套循环的RxJava解决方案
* {@link #method12()}
* Student->ArrayList
*/
private void method13() {
Observable.from(DataFactory.getData())
.map(new Func1>() {
@Override
public ArrayList call(Student student) {
return student.courses;
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1>() {
@Override
public void call(ArrayList courses) {
for (int i = 0; i < courses.size(); i++) {
Logger.d("观察者:" + courses.get(i).name);
}
}
});
}
看到观测者subscriber处理事件响应时,是把List
通过循环来处理众多的事件对于响应式编程来说是冗繁而且缺乏规范的,我想正是因为List数据源是有厚度的,我们需要用某种方法把这种厚度给抹平,把原本一个整体弹出的数据源想办法转换为单个独立弹出的数据,所以才有了flat——扁平化的map。
先把结论写出来,如果用了flatmap,对比是非常明显的:
/**
* 需要:输出每一个学生选修的课程,对method13的简化
* 嵌套循环的RxJava解决方案
* {@link #method13()}
* Student -> ArrayList -> Observable ->
*/
private void method14() {
// Student->Course
Observable.from(DataFactory.getData())
.flatMap(new Func1>() {
@Override
public Observable call(Student student) {
return Observable.from(student.courses);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1() {
@Override
public void call(Course course) {
Logger.d("观察者:" + course.name);
}
});
这个subscriber不再需要处理一整块List
如果仔细看flatmap的诸多重载方法,其核心都是merge方法的使用。谨以以上代码举例,实际使用的是这个flatmap:
public final Observable flatMap(Func1 super T, ? extends Observable extends R>> func) {
if (getClass() == ScalarSynchronousObservable.class) {
return ((ScalarSynchronousObservable)this).scalarFlatMap(func);
}
return merge(map(func));
}
merge方法的作用是将多个observalbe源数据整合到一个observable中。这里func()的实现必须是从数据源类型向被观测者Observalbe类型的转换,简单的说,这里的map映射中做了个二次转换,将原来的数据源T转换为数据源R之后,通过operator创建操作构造了一个相关observalbe。
然后再把这些个observable弹出来的数据统统整合到一个observable中去。
Rx和RxJava文档中文翻译项目