RXJava最爽的莫过于链式编程,代码条理清晰,可以把各种回调嵌套、时序混乱、类型转换等的问题以一条链式调用统统搞定。而这么神奇的功能就需要操作符来支持。
看一段简单的代码:
Observable.just("张三","李四","王五").subscribe(new Action1<String>()
{
@Override public void call(String s)
{
Log.d("name",s);
}
});
就是输出几个简单的字符串,其中just就是一个操作符(同理create、from)也是。在我理解中,操作符就是在数据流中,对数据进行各种转换、处理的一些封装好的方法。
而我们常用的操作符,除了刚才说的create、from、just这些,最常用的就是用于数据转换处理(也可以用于其他用途)的map和flatMap了。除此之外,还有可以帮我们解决搜集多个API结果状态的merge啊(吐血推荐)、防抖throttleFirst啊(再也不用不停地getCurrentTime了)等等。。。
Map
如果有一个模块,可以获取到每个用户的所有信息,然后在UI上我需要绘制用户的头像,这样需要在用户信息里获取到头像的Url。这个用户信息包括了:
class UserInfoBean
{
public String avatar;
public String name;
public String phoneNum;
}
那如果按照正常的办法,我们会这样去完成:
ImageView ivAvatar = (ImageView)findViewById(R.id.iv_user_avatar);
Observable.just(userInfo).subscribe(new Action1<UserInfoBean>()
{
@Override public void call(UserInfoBean bean)
{
Glide.with(mContext).load(bean.avatarUrl).into(ivAvatar);
}
});
讲道理说subscriber中应该只响应结果,这里的subsriber只期望获取一个url用来展示头像,而对其他的东西并不感兴趣,那么好吧,修改一下代码:
ImageView ivAvatar = (ImageView)findViewById(R.id.iv_user_avatar);
Observable.just(userInfo.avatarUrl).subscribe(new Action1<String>()
{
@Override public void call(String url)
{
Glide.with(mContext).load(url).into(ivAvatar);
}
});
在这里,我们使observable中调用next的时候直接传入一个头像的Url,这样不就好了嘛~ 可是,如果我要在其他地方需要获取到用户的名字怎么办?现在这个Observable中直接返回的是头像Url啊 ,难道我要重写一个吗?
这时候就可以用到map了。
ImageView ivAvatar = (ImageView)findViewById(R.id.iv_user_avatar);
TextView tvName = (TextView)findViewById(R.id.tv_user_name);
//更新头像
getUserInfo().map(new Func1<UserInfoBean,String>()
{
@Override public String call(UserInfoBean bean)
{
return bean.avatarUrl;
}
}).subscribe(new Action1<String>()
{
@Override public void call(String url)
{
Glide.with(mContext).load(url).info(ivAvatar);
}
});
//更新用户昵称
getUserInfo().map(new Func1<UserInfoBean,String>()
{
@Override public String call(UserInfoBean bean)
{
return bean.name;
}
}).subscribe(new Action1<String>()
{
@Override public void call(String name)
{
tvName.setText(name);
}
});
Observable getUserInfo()
{
return Observable.just(userInfo);
}
map可以把obserable传出的数据做一个转换再放到subscriber的onNext方法中执行~~~这样既实现了observable的复用,又使得subcriber中的工作减轻,最重要的是提高了代码的逼格。
flatMap
也行你会觉得map没啥用,甚至有些画蛇添足,那么看下接下来的这个进阶加强版。
还是刚才的那个例子,现在每个用户有若干好友,那么需要在userInfo中加入一个List,这里存放着所有好友的用户Id
class UserInfoBean
{
public String avatarUrl;
public String name;
public String phoneNum;
public List<String> friendList;
}
现在我想获取这个用户所有好友的姓名,那好,那我正常情况下肯定是需要遍历这个好友列表,获取每个好友的id然后去查询好友姓名吧。像这样
UserInfoBean userInfoBean = getCurrentUser();
Observable.just(userInfoBean).subscribe(new Action1<UserInfoBean>()
{
@Override public void call(UserInfoBean bean)
{
for(String id:bean.friendList)
{
UserInfoBean friend = queryUserById(id);
System.out.print(friend.name);
}
}
});
为什么我都用RXJava这么洋气的东西了,还要在响应方法中写这么多的代码呢,而且还用到了for each!好吧,解决这个问题就用到flatMap了!
先看使用flatMap改写的代码!
Observable.just(userInfoBean).flatMap(new Func1<UserInfoBean, Observable<String>>() {
@Override
public Observable<String> call(UserInfoBean userInfoBean)
{
List<String> names = new ArrayList<String>();
for(String id:userInfoBean.friendList)
{
UserInfoBean friend = queryUserById(id);
names.add(friend.name);
}
return Observable.from(names);
}
}).subscribe(new Action1<String>() {
@Override
public void call(String name) {
System.out.print(name);
}
});
在flatMap的参入中放入一个Func1对象!这个Fucn1对象封装了个有一个入参并有返回值call方法。之前还有个Action封装的是不同入参但没有返回值的call方法,现在对比下,顾名思义,func作为一个方法,需要有返回值,所以funcX都是封装了不同数量入参且有一个返回值的方法。而Action作为一个动作,不需要有反馈,所以ActionX封装的是不同数量入参且没有返回值的方法。
接着看flatMap中func1的call方法做了些什么。在call方法中遍历了所有好友的id,然后根据Id查询出好友的姓名,然后通过from将姓名返回,这样就可以通过from来实现了1对多的一个转换,而且把所有耗时的逻辑操作都封装到异步方法中执行,使得subsciber只去响应。
从这可以看出,flatMap中存放的是一个转换的操作,但是这个转换的操作是在什么时候执行调用的呢?而且func1中的call返回的为什么是一个observbale对象?这块儿我觉得理解起来还是有一定难度的,至少对于我来讲,耗费了不少的时间来理解。
这地方建议参考扔物线的文章(最上面的链接)并结合源码来看,我在这边简单的把我个人的理解记录下。
首先,点开flatMap的源码:
public final <R> Observable<R> flatMap(Func1<? super T, ? extends Observable<? extends R>> func) {
if (getClass() == ScalarSynchronousObservable.class) {
return ((ScalarSynchronousObservable<T>)this).scalarFlatMap(func);
}
return merge(map(func));
}
忽略同步的那个判断,直接看return,发现这里有两个方法,分别是merge和map,点开map:
public final <R> Observable<R> map(Func1<? super T, ? extends R> func) {
return lift(new OperatorMap<T, R>(func));
}
这里注意两点:1.利用func创建了一个OperatorMap(一个实现了Operator接口的类)。2.将这个OperatorMap放入List中。看到Operator了吧,这下和我们这篇文章的题目贴切了吧~~~ 而且List这个东西这的是整个转换操作符的精华,这里的代码很少,但是很重要:
public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
return new Observable<R>(new OnSubscribe<R>() {
@Override
public void call(Subscriber<? super R> o) {
try {
Subscriber<? super T> st = hook.onLift(operator).call(o);
try {
// new Subscriber created and being subscribed with so 'onStart' it
st.onStart();
onSubscribe.call(st);
} catch (Throwable e) {
// localized capture of errors rather than it skipping all operators
// and ending up in the try/catch of the subscribe method which then
// prevents onErrorResumeNext and other similar approaches to error handling
Exceptions.throwIfFatal(e);
st.onError(e);
}
} catch (Throwable e) {
Exceptions.throwIfFatal(e);
// if the lift function failed all we can do is pass the error to the final Subscriber
// as we don't have the operator available to us
o.onError(e);
}
}
});
}
可以看到list传入了一个operator对象,返回了一个Observable对象。我们上面使用flatMap的例子中,我们自己定义了一个输出某个用户信息的Observable为ObservableA,打印这个用户所有好友姓名的subscriber为subscriberB。那么设这个List中生成的Observable对象为ObserableB,看下这个ObservableB中的call方法,这个call方法中利用传入的operator和原始的subsriberA生成了一个新的subscriber对象为subsriberB,然后调用OnSubsribe.call来注册这个新的subsriberB。Ok,那么看一下这个subseriberB都做了什么,这就要先看下OperatorMap这个类中做了什么。
public final class OperatorMap<T, R> implements Operator<R, T> {
final Func1<? super T, ? extends R> transformer;
public OperatorMap(Func1<? super T, ? extends R> transformer) {
this.transformer = transformer;
}
@Override
public Subscriber<? super T> call(final Subscriber<? super R> o) {
return new Subscriber<T>(o) {
@Override
public void onCompleted() {
o.onCompleted();
}
@Override
public void onError(Throwable e) {
o.onError(e);
}
@Override
public void onNext(T t) {
try {
o.onNext(transformer.call(t));
} catch (Throwable e) {
Exceptions.throwOrReport(e, this, t);
}
}
};
}
}
这个构造方法中的入参就是我们在flatMap中定义的func1,而call方法的入参就是我们的subsriberA,会发现在这里call方法中,新建了一个subsriber,这个subsriber就是subsriberB,而这个subsriberB其实就是对subsriberA进行了一个带来,其中OnComplete和OnError都是直接调用A的,只有在onNext中做了个转换,而这个转换的方法就是func1中的call方法。也就是说,现在其实是subscriberB订阅了observbaleA,而subcriberB在获取了ObservableA的处理结果后,经过转换,又发送给了ObserableA。而此时,subsriberA订阅的就不是ObserableA,而是ObserableB了,也就是说B在A之间做了一个代理的作用。