RxJava 的变换操作符主要包括以下几种:
- map:对序列的每一项都用一个函数来变换 Observable 发射的数据序列
- flatMap、 concatMap 和 flatMapIterable:将 Observable 发射的数据集合变换为 Observables 集合,然后将这些 Observable 发射的数据平坦化地放进一个单独的 Observable 中
- switchMap :将 Observable 发射的数据集合变换为 Observables 集合,然后只发射这些 Observables 最近发射过的数据。
- scan :对 Observable 发射的每一项数据应用一个函数,然后按顺序依次发射每一个值
- groupBy :将 Observable 拆分为 Observable 集合,将原始 Observable 发射的数据按 Key 分组,每一个 Observable 发射过一组不同的数据
- buffer :定期从 Observable 收集数据到一个集合,然后把这些数据集合打包发射,而不是一次发射一个
- window :定期将来自 Observable 的数据拆分成一些 Observable 窗口,然后发射这些窗口,而不是每次发射一项
- cast :在发射之前强制将 Observable 发射的所有数据转换为指定类型
1. map 和 flatMap
1.1 map 操作符
对 Observable 发射的每一项数据应用一个函数,执行变换操作
map 操作符对原始 Observable 发射的每一项数据应用一个你选择的函数,然后返回一个发射这些结果的 Observable
RxJava 将这个操作符实现为 map 函数,这个操作符默认不在任何特定的调度器上执行。
Observable.just("HELLO")
.map(new Function() {
@Override
public String apply(String s) throws Exception {
return s.toLowerCase();
}
})
.map(new Function() {
@Override
public String apply(String s) throws Exception {
return s + " world!";
}
})
.subscribe(new Consumer() {
@Override
public void accept(String s) throws Exception {
Log.d(TAG, "Next: " + s);
}
});
// 执行结果
Next: hello world!
第一次转换,将字符串 “HELLO“ 转换成全是小写字母的字符串 ”hello“。第二次转换,在字符串 ”hello“ 后面添加新的字符串 ” world!“,它们组成了新的字符串也就是执行结果。
1.2 flatMap 操作符
flatMap 将一个发射数据的 Observable 变换为多个 Observables, 然后将它们发射的数据合并后放进一个单独 Observable
flatMap 操作符使用一个指定的函数对原始 Observable 发射的每一项数据执行变换操作,这个函数返回一个本身也发射数据 Observable,然后 flatMap 合并这些 Observables 发射的数据,最后将合并后的结果当作它自己的数据序列发射。
下面看一个例子。先定义一个用户对象,包含用户名和地址,由于地址可能会包括生活、工作等地方,所以使用一个 List 对象来表示用户的地址
public class User {
public String username;
public List addresses;
public static class Address {
public String street;
public String city;
}
}
如果想打印出某个用户所有的地址,那么可以借助 map 操作符返回一个地址的列表。
Observable.just(user)
.map(new Function>() {
@Override
public List apply(User user) throws Exception {
return user.addresses;
}
})
.subscribe(new Consumer>() {
@Override
public void accept(List addresses) throws Exception {
for (User.Address address : addresses) {
Log.d(TAG, "Next: " + address.city);
}
}
});
// 执行结果
Next: shang hai
Next: su zhou
换成 flatMap 操作符之后,flatMap 内部将用户的地址列表转换成一个 Observable
Observable.just(user)
.flatMap(new Function>() {
@Override
public ObservableSource apply(User user) throws Exception {
return Observable.fromIterable(user.addresses);
}
})
.subscribe(new Consumer() {
@Override
public void accept(User.Address address) throws Exception {
Log.d(TAG, "Next: " + address.city);
}
});
// 执行结果
Next: shang hai
Next: su zhou
flatMap 对这些 Observables 发射的数据做的是合并 (merge) 操作, 因此它们可能是交错的。还有一个操作符不会让变换后 Observables 发射的数据交错,它严格按照顺序发射这些数据,这个操作符就是 concatMap
2. groupBy
groupBy 操作符将一个 Observable 拆分为一些 Observables 集合,它们中的每一个都发射原始
Observable 的一个子序列
哪个数据项由哪一个 Observable 发射是由一个函数判定的,这个函数给每一项指定一个 Key, Key 相同的数据会被同一个 Observable 发射。
最终返回的是 Observable 一个特殊子类 GroupedObservable 。它是一个抽象类。getKey()
方法是 GroupedObservable 方法,这个 Key 用于将数据分组到指定的 Observable
Observable.range(1, 8)
.groupBy(new Function() {
@Override
public String apply(Integer integer) throws Exception {
return integer % 2 == 0 ? "偶数组" : "奇数组";
}
})
.subscribe(new Consumer>() {
@Override
public void accept(GroupedObservable stringIntegerGroupedObservable) throws Exception {
Log.d(TAG, "Next-> group name: " + stringIntegerGroupedObservable.getKey());
}
});
// 执行结果
Next-> group name: 奇数组
Next-> group name: 偶数组
对上述代码做一些修改, 对 GroupedObservable 使用 getKey() 方法,从而能够选出奇数组的
GroupedObservable ,最后打印出该 GroupedObservable 下的全部成员
Observable.range(1, 8)
.groupBy(new Function() {
@Override
public String apply(Integer integer) throws Exception {
return integer % 2 == 0 ? "偶数组" : "奇数组";
}
})
.subscribe(new Consumer>() {
@Override
public void accept(GroupedObservable stringIntegerGroupedObservable) throws Exception {
final String key = stringIntegerGroupedObservable.getKey();
if (key.equals("奇数组")) {
stringIntegerGroupedObservable.subscribe(new Consumer() {
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, "Next->" + key + ":" + integer);
}
});
}
}
});
// 执行结果
Next->奇数组:1
Next->奇数组:3
Next->奇数组:5
Next->奇数组:7
3. buffer 和 window
3.1 buffer 操作符
buffer 会定期收集 Observable 数据并放进一个数据包裹,然后发射这些数据包裹,而不是一次发射一个值
buffer 操作符将 Observable 变换为另一个,原来的 Observable 正常发射数据,由变换产生 Observable 发射这些数据的缓存集合。
Observable.range(1, 10)
.buffer(2)
.subscribe(new Consumer>() {
@Override
public void accept(List integers) throws Exception {
Log.d(TAG, "Next: " + integers);
}
}, new Consumer() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.d(TAG, "Error: " + throwable);
}
}, new Action() {
@Override
public void run() throws Exception {
Log.d(TAG, "Complete.");
}
});
// 执行结果
Next: [1, 2]
Next: [3, 4]
Next: [5, 6]
Next: [7, 8]
Next: [9, 10]
Complete.
上述代码,发射了从 1 到 10 这 10 个数字,由于使用了 buffer 操作符,它会将原先的 Observable 转换成新的 Observable,而新的 Observable 每次可发射两个数字,发射完毕后调用 onComplete()
方法。
查看 buffer 操作符的源码,可以看到使用 buffer 操作符之后转换成
public final Observable> buffer(int count) {
return buffer(count, count);
}
如果将发射的数据变成11,range(1, 11) 执行结果如下:
Next: [1, 2]
Next: [3, 4]
Next: [5, 6]
Next: [7, 8]
Next: [9, 10]
Next: [11]
Complete.
再修改一下代码,缓存5个数字,执行结果如下:
Next: [1, 2, 3, 4, 5]
Next: [6, 7, 8, 9, 10]
Next: [11]
Complete.
在 RxJava 有许多 buffer 的重载方法,例如比较常用的 buffer(count, skip)
buffer(count, skip) 从原始 Observable 的第一项数据开始创建新的缓存,此后每当收到 skip 项数据,就用 count 项数据填充缓存: 开头的一项和后续的 count - 1 项。它以列表 (List) 的形式发射缓存,这些缓存可能会有重叠部分 (比如 skip < count),也可能会有间隙(比如 skip > count时),取决于 count 和 skip 的值。
Observable.range(1, 11)
.buffer(5, 1)
.subscribe(new Consumer>() {
@Override
public void accept(List integers) throws Exception {
Log.d(TAG, "Next: " + integers);
}
}, new Consumer() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.d(TAG, "Error: " + throwable);
}
}, new Action() {
@Override
public void run() throws Exception {
Log.d(TAG, "Complete.");
}
});
// 执行结果
Next: [1, 2, 3, 4, 5]
Next: [2, 3, 4, 5, 6]
Next: [3, 4, 5, 6, 7]
Next: [4, 5, 6, 7, 8]
Next: [5, 6, 7, 8, 9]
Next: [6, 7, 8, 9, 10]
Next: [7, 8, 9, 10, 11]
Next: [8, 9, 10, 11]
Next: [9, 10, 11]
Next: [10, 11]
Next: [11]
Complete.
如果原来 Observable 发射了一个 onError 通知,那么 buffer 会立即传递这个通知,而不是首先发射缓存的数据,即使在这之前缓存中包含了原始 Observable 发射的数据。
window 操作符与 buffer 类似, 但它在发射之前是把收集到的数据放进单独的 Observable,而不是放进 一个数据结构。
3.2 window 操作符
定期将来自原始 Observable 的数据分解为一个 Observable 窗口,发射这些窗口,而不是每次发射一项数据
window 发射的不是原始 Observable 数据包,而是 Observables,这些 Observables 中的每一个都发射原始 Observable 数据的一个子集,最后发射一个 onComplete 通知。
Observable.range(1, 10)
.window(2)
.subscribe(new Consumer>() {
@Override
public void accept(Observable integerObservable) throws Exception {
Log.d(TAG, "Next-> ");
integerObservable.subscribe(new Consumer() {
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, " " + integer);
}
});
}
}, new Consumer() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.d(TAG, "Error: " + throwable);
}
}, new Action() {
@Override
public void run() throws Exception {
Log.d(TAG, "Complete.");
}
});
// 执行结果
Next->
1
2
Next->
3
4
Next->
5
6
Next->
7
8
Next->
9
10
Complete.
如果我的文章对您有帮助,不妨点个赞鼓励一下(^_^)