这可能是你看过的最清晰明了的讲解
写在前面:
RxJava
新手可能会对map、flatMap
等一系列对操作符产生疑问,但有些例子又很难懂,这里通过最通俗易懂
的方法为你讲述它们究竟是什么东西,至于以后如何用,你要先知道它们的作用了解它们的用法,用什么是你的需求决定的,所以,当你了解的多了自然知道用那个最合适,关键是了解
。
正文 👇
map
先说
map
,一个简单的例子 👇
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
})
.map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) throws Exception {
Log.e(TAG,"开始净化处理");
return integer.toString() + "号水源净化中 => 净化完毕";
}
})
.subscribe(new Observer<String>() {
@Override
public void onNext(String s) {
Log.e(TAG,"接收到净化完毕的水【" + s + "】");
}
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});
// Log日志:
// 开始净化处理
// 接收到净化完毕的水【1号水源净化中 => 净化完毕】
// 开始净化处理
// 接收到净化完毕的水【2号水源净化中 => 净化完毕】
// 开始净化处理
// 接收到净化完毕的水【3号水源净化中 => 净化完毕】
- 非通俗定义:map 操作符对原始 Observable 发射对每一项数据应用一个你选择对函数,然后返回一个发射这些结果对 Observable
如上,清晰明了,将
1、2、3
水源进行净化处理,最终接收到处理完毕到水,map
相当于一个中介,onNext(1)
发送的可以是你以后项目中服务器端获取到的数据,通过map
这个中介处理,处理后的数据发送到最终接收的地方(Observer 的 onNext 方法中
)当然可能就是以后项目中【先接收服务器端的数据(比如接收到
int
数据)】=>【经过 map 这个中介处理成你想要的数据(转成String
)】=>【更新到 UI 上(设置到textView
上)】
flatMap
这里说
flatMap
- 非通俗定义:将一个发送事件的
Observable(被观察者)
变换为发送多个事件的Observables
,然后将他们发射的事件合并后放进一个单独的Observable
里,需要注意的是flatMap
并不保证事件顺序,也就是说转换之后的Observables
的顺序不必与转换之前的序列的顺序一致
// 先是两个很简单的类
class ClassRoom {
String classRoomName; // 教室的名字
List<Student> list = new ArrayList<>(); // 教室中的学生列表
}
class Student {
Student(String n) {
this.name = n;
}
String name;// 学生的名字
}
// 添加简单的测试数据
List<ClassRoom> school = new ArrayList<>();
// 1班
ClassRoom c1 = new ClassRoom();
c1.classRoomName = "1班";
c1.list.add(new Student("学生A"));
c1.list.add(new Student("学生B"));
c1.list.add(new Student("学生C"));
c1.list.add(new Student("学生D"));
// 2班
ClassRoom c2 = new ClassRoom();
c2.classRoomName = "2班";
c2.list.add(new Student("学生E"));
c2.list.add(new Student("学生F"));
c2.list.add(new Student("学生G"));
c2.list.add(new Student("学生H"));
// 3班
ClassRoom c3 = new ClassRoom();
c3.classRoomName = "3班";
c3.list.add(new Student("学生I"));
c3.list.add(new Student("学生J"));
c3.list.add(new Student("学生K"));
c3.list.add(new Student("学生L"));
// 添加
school.add(c1);
school.add(c2);
school.add(c3);
// 关键部分
Observable.fromIterable(school)
.flatMap(new Function<ClassRoom, ObservableSource<Student>>() {
@Override
public ObservableSource<Student> apply(ClassRoom classRoom) throws Exception {
Log.e(TAG, "ClassRoom name:" + classRoom.classRoomName);
return Observable.fromIterable(classRoom.list).delay(10, TimeUnit.MILLISECONDS);
}
})
.subscribe(new Observer<Student>() {
@Override
public void onNext(Student student) {
Log.e(TAG, "学生姓名:" + student.name);
}
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
});
首先
fromIterable
是什么:相当于 👇
onNext(school.get(0));
onNext(school.get(1));
onNext(school.get(2));
onComplete();
那么
Observable.fromIterable(classRoom.list).delay(10, TimeUnit.MILLISECONDS);
是什么?相当于原有的基础上在发送之前加了10
毫秒的延迟,TimeUnit
表示事件单位,这里用的是MILLISECONDS
也就是毫秒,秒则是SECONDS
,这里加一个延迟主要是为了模拟网络延迟的效果
看一下日志
// 加了 10 毫秒延迟的
ClassRoom name:1班
ClassRoom name:2班
ClassRoom name:3班
学生姓名:学生A
学生姓名:学生C
学生姓名:学生B
学生姓名:学生E
学生姓名:学生D
学生姓名:学生G
学生姓名:学生F
学生姓名:学生H
学生姓名:学生J
学生姓名:学生K
学生姓名:学生I
学生姓名:学生L
// 不加延迟的
ClassRoom name:1班
学生姓名:学生A
学生姓名:学生B
学生姓名:学生C
学生姓名:学生D
ClassRoom name:2班
学生姓名:学生E
学生姓名:学生F
学生姓名:学生G
学生姓名:学生H
ClassRoom name:3班
学生姓名:学生I
学生姓名:学生J
学生姓名:学生K
学生姓名:学生L
可以看到,就像上面
非通俗定义
中所说的并不保证事件的顺序(A、C、B、E、D、G...
),加延迟主要是为了更好的显现出它不保证顺序的特性,并不是因为有了延迟才不保证顺序,而是无论如何flatMap
都不保证你接收到事件的顺序我们这个例子相当于
一个学校
中有3
个班级,我们想要打印出所有班级的名字
以及每个班级
中每个学生的名字
,但是你可能会看到觉得代码反而变多了
- 首先这只是个例子
-
RxJava
并不是说让代码变少,而是让代码逻辑更清晰,试想清晰的代码逻辑和少又难懂又嵌套了各种循环的代码那个更好呢 - 当你了解的操作符多了,在项目中遇到实际问题你自然知道哪个最适合你
通过这个例子可以看出,
flatMap
是将发送事件的Observable
经过flatMap
变换为多个发送事件的Observables
然后再将他们发射后的事件合并后放进一个单独的Observable
里,看下图 👇
再来看更加详细的分解 👇
由上图再根据我们的例子得知:
c1、c2、c3
经过flatMap
后,依次经历了Observable1、2、3(图中椭圆形的内容)
,打印了一下班级名,然后又分别把他们转换为了StudentA - StudentL
,其中StudentA - StudentD
由c1
得到,以此类推。想必此时已经清晰明了了,当然现在你已经知道
flatMap
并不保证事件的顺序,如果想要保证顺序的话需要使用concatMap
。
使用场景
map
适用于一对一转换
flatMap
适用于一对多,多对多