1.前言
在前面的一篇文章中介绍了RxJava创建操作符的用法,如果对创建操作符不是很清楚的话建议去看看RxJava—创建操作符。在本章会开始聊聊RxJava中的转换操作符(Operators),转换操作符是为了解决对Observable对象的变换的问题,操作符用于在Observable和最终的Subscriber之间修改Observable发出的事件,RxJava中给我们提供了很多有用的操作符,在本章的内容中主要介绍转换操作符,常见的转换操作符有:Map()、FlatMap()、ConcatMap()、FlatMapIterable()、SwitchMap()、Scan()、GroupBy()等。
在本章的内容中主要介绍转换操作符,所有的这些操作符都是作用于一个可观测序列,然后变换它的发射值,最后用一种新的形式返回他们。
2.转换操作符的使用
(1)Map
map()函数接收一个Func1类型的参数(map(Func1<? super T, ? extends R> func)),然后把这个Func1应用到每一个Observable发射的值上,将发射值转换为我们所期望的值。(PS:Func1跟在RxJava—入门介绍中所提到的ActionX是相似的,都是实现不完整定义的回调,ActionX或者FuncX后面的X指的是传入的参数个数,只不过ActionX是没有返回值的,而FuncX是有返回值的)。
根据官方给出的原理图:
如果我们需要将一组数据转换称为字符串,我们就可以通过map这样实现:
mapButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
Observable.just(1, 2, 3, 4, 5)
.map(new Func1<Integer, String>()
{
public String call(Integer integer)
{
return "This is" + integer;
}
})
.subscribe(new Action1<String>()
{
public void call(String s)
{
Log.d("map", s);
}
});
}
});
从上面的代码中,Func1的两个参数分别表示的是Observable的发射值的类型和map转换后的类型,上面的代码中发射的值是Integer类型,通过map转换后称为String类型。当点击按钮后在控制台上,可以看到这样的结果:
(2)FlatMap
flatMap()同样也是用于做转换的,但是作用是不一样的,map()是把一种类型转换称为另一种类型的数据,而flatMap()是把传入的一种类型的数据转换成为一个Observable对象。
根据官方给出的原理图:
假设我们现在要打印出所有学生的全部成绩,如果使用map()来进行操作就需要在内部嵌套一个for循环
Observable.from(studentList)
.map(new Func1<Student, List<Integer>>()
{
public List<Integer> call(Student student)
{
return student.getScoreList();
}
})
.subscribe(new Action1<List<Integer>>()
{
public void call(List<Integer> integers)
{
for(int i = 0; i < integers.size(); i++)
{
Log.d("flatMap", "分数为:" + integers.get(i));
}
}
});
使用for循环来实现显然是违背了RxJava的链式编程的思想,用map
()显示不是很合理的,因为map()是一对一的转化,而我现在的要求是一对多的转化。那么我们可以使用flatMap来实现。
Observable.from(studentList)
.flatMap(new Func1<Student, Observable<Integer>>()
{
public Observable<Integer> call(Student student)
{
return Observable.from(student.getScoreList());
}
})
.subscribe(new Action1<Integer>()
{
public void call(Integer integer)
{
Log.d("flatMap", "分数为:" + integer);
}
});
从前面的两个例子中你肯定发现了,flatMap()和map()都是把传入的参数转化之后返回另一个对象。但和map()不同的是,flatMap()中返回的是Observable对象,并且这个Observable对象并不是被直接发送到 Subscriber的回调方法中。
注意,flatMap()是把几个新的Observable合并为一个Observable返回, 只要这些新的Observable有数据发射出来, flatMap()就会把数据立刻发射出去。所以如果这些新的Observable发射数据是异步的,那么flatMap()返回的数据也是异步的。
(3)ConcatMap
concatMap()解决了flatMap()的交叉问题,它能够把发射的值连续在一起。
根据官方给出的原理图:
flatMap()操作符使用你提供的原本会被原始Observable发送的事件,来创建一个新的Observable,而且这个操作符,返回的是一个自身发送事件并合并结果的Observable,可以用于任何由原始Observable发送出的事件,发送合并后的结果。但是,flatMap()可能交错的发送事件,最终结果的顺序可能并是不原始Observable发送时的顺序,为了防止交错的发生,可以使用与之类似的concatMap()操作符。
如果你不想把新Observable中的数据交织在一起发射,则可以选择使用concatMap函数,该函数会等第一个新的 Observable完成后再发射下一个新的Observable中的数据。
(4)FlatMapIterable
flatMapIterable()和flatMap()类似,区别是flatMap()参数把每个数据转换为一个新的Observable,而 flatMapIterable()参数把一个数据转换为一个新的iterable对象。
根据官方给出的原理图:
假设我们现在要打印出所有学生的全部成绩,也可以通过flatMapIterable()来实现
Observable.from(studentList)
.flatMapIterable(new Func1<Student, Iterable<Integer>>()
{
public Iterable<Integer> call(Student student)
{
return student.getScoreList();
}
})
.subscribe(new Action1<Integer>()
{
public void call(Integer integer)
{
Log.d("flatMapIterable", "分数为:" + integer);
}
});
(5)SwitchMap
switchMap()和flatMap()很像,除了一点:每当源Observable发射一个新的数据项(Observable)时,它将取消订阅并停止监视之前那个数据项产生的Observable,并开始监视当前发射的这一个。
根据官方给出的原理图:
(6)Scan
scan()对一个序列的数据应用一个函数,并将这个函数的结果发射出去的结果做为下个数据应用合格函数时的第一个参数使用。
根据官方给出的原理图:
使用scan()来看一个简单的例子
Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9)
.scan(new Func2<Integer, Integer, Integer>()
{
public Integer call(Integer integer, Integer integer2)
{
return integer + integer2;
}
})
.subscribe(new Action1<Integer>()
{
public void call(Integer integer)
{
Log.d("scan", String.valueOf(integer));
}
});
计算出的结果为:1, 3, 6, 10, 15, 21, 28, 36, 45
(7)GroupBy
groupBy()将原始Observable发射的数据按照key来拆分成一些小的Observable,然后这些小Observable分别发射其所包含的的数据,和SQL中的groupBy类似。实际使用中,我们需要提供一个生成key的规则(也就是Func1中的call方法),所有key相同的数据会包含在同一个小的Observable中。另外我们还可以提供一个函数来对这些数据进行转化,有点类似于集成了flatMap。
根据官方给出的原理图:
我们现在需要根据学生的性别输出学生的姓名和学号,就可以使用到groupBy()
Observable<GroupedObservable<String, Student>> groupByItem = Observable.from(studentList)
.groupBy(new Func1<Student, String>()
{
public String call(Student student)
{
return student.getGender();
}
});
Observable.concat(groupByItem)
.subscribe(new Action1<Student>()
{
public void call(Student student)
{
Log.d("groupBy", "学号" + student.getNumber()
+ " 姓名" + student.getName()
+ " 性别" + student.getGender());
}
});
通过上面的代码我们创建了一个新的groupByItem,它将会发送一个带有GroupedObservable的序列(也就是指发送的数据项的类型为GroupedObservable)。GroupedObservable是一个特殊的Observable,它基于一个分组的key,在这个例子中的key就是学生的性别。
当点击按钮后在控制台上,可以看到这样的结果:
以上Demo的源代码地址:点击打开链接