【旧】如何实现 RxJava 的链式调用 -- map 方法的实现
写于 2017年 05 月 24 日
public interface Api{
void queryPhoto(String query,QueryCallback QueryCallback);
interface QueryCallback{
void onQuerySuccess(List<Photo> photoList);
void onQueryFailed(Exception e);
}
}
public class PhotoUtils{
Api mApi;
public void handlePhotoSync(){
mApi.queryPhoto("flyer", new Api.QueryCallback() {
@Override
public void onQuerySuccess(List<Photo> photoList) {
Photo photo = getBestPhoto(photoList);
String imgUrl = photo.getUrl();
}
@Override
public void onQueryFailed(Exception e) {
ToastUtils.show(e);
}
});
}
}
此时假设有一个包裹类,他可以运行 getBestPhoto(photoList)
和 photo.getUrl()
这两行代码,而这两行代码由外部放入
那么调用就会如下:
RxJob<List<Photo>> queryPhotoJob = new RxJob<List<Photo>>(){
@Override
public void doJob(final RxCallback<List<Photo>> rxCallback) {
mApi.queryPhoto("flyer", new Api.QueryCallback() {
@Override
public void onQuerySuccess(List<Photo> photoList) {
rxCallback.onNext(photoList);
}
@Override
public void onQueryFailed(Exception e) {
rxCallback.onError(e);
}
});
}
};
};
// 如果有这么一个包裹类,代码就可以写成下面这样,则当需要调用的时候,
// 代码就会和 RxJava 很类似( java 8 上会变成 Lambda 表达式 )
// 从而使得代码具有很清晰的逻辑,异步代码看上去也会变的如同步代码一样。
queryPhotoJob
.map(photoList -> { return getBestPhoto(photoList);})
//.map(new Func<List<Photo>,Photo>()
// @Overide
// Photo call(List<Photo> photoList){
// return getBestPhoto(photoList);
// })
.map(photo -> {return photo.getUrl()})
.doJob(new RxCallback<T>() {
@Override
public void onNext(T t) {
}
@Override
public void onError(Exception e) {
}
});
因此接下来需要考虑如何实现这个包裹的类,使得其调用会像 RxJava
一般。首先需要确定该类需要处理以下几个问题
- 该包裹的类有哪些方法及其使用
map
函数如何实现,其调用逻辑
整体介绍:
确定一个包裹类 —> 创建包裹类 new RxJob()
—> map
方法实现
map
方法实现包括 :
入参和出参的基本类型确定 —> 转变函数的确定 —> doJob
调用 —> map
返还值具体介绍 —> func.call()
返还值的传递
包裹类方法和作用
首先取名,该类叫 RxJob
RxJob
类的意义,它被定义成一个任务,类似 AsyncTask
,而运行该任务的方法就是 doJob
有入参 RxCallback
类的定义还有一个泛型 T
,主要决定了 doJob
方法中 RxCallback<T>
中的 T
类型
该类型会影响很多部分的参数类型,具体需要参考后面的分析。
代码如下:
public abstract class RxJob<T> {
public abstract void doJob(RxCallback<T> rxCallback);
public <R> RxJob<T> map(final Func<T,R> func){...}
}
先忽略 map
方法,现看 doJob(RxCallback<T> rxCallback)
doJob(RxCallback<T> rxCallback)
有一个回调,回调接口定义如下:
public interface RxCallback<T> {
void onNext(T t);
void onError(Exception e);
}
两个方法,onNext(T t)
和 onError(Exception e)
,一个用来传递内容 T
,一个用来处理错误
也就是说,RxJob
执行的内容最终都是 doJob(RxCallback<T> rxCallback)
方法,这个方法是链接起所有回调的核心
保证了所有的方法能一个一个根据链式调用一个一个执行
创建包裹类 new RxJob(..)
直接带入当前的栗子中,代码如下
RxJob<List<Photo>> queryPhotoJob = new RxJob<List<Photo>>(){
@Override
public void doJob(final RxCallback<List<Photo>> rxCallback) {
mApi.queryPhoto("flyer", new Api.QueryCallback() {
@Override
public void onQuerySuccess(List<Photo> photoList) {
rxCallback.onNext(photoList);
}
@Override
public void onQueryFailed(Exception e) {
rxCallback.onError(e);
}
});
}
};
};
其中RxJob<T>
中的 T
的值是 List<Photo>
,这主要决定于 mApi.queryPhoto()
中 onQuerySuccess(List<Photo> photoList)
中的参数类型,让 rxCallback.onNext(photoList)
能取到对应的类型。从而把拿到的数据内容,借助 RxCallback
回调往外层抛出。
即,如果我们需要调用 queryPhotoJob
,让他执行对应的操作代码如下
queryPhotoJob.doJob(new RxCallback<List<Photo>>(){
public void onNext(List<Photo> photoList){
Photo photo = getBestPhoto(photoList);
String imgUrl = photo.getUrl();
}
public void onError(Exception e){...}
});
核心部分 map 函数如何实现
首先可以确定的是 map 函数返回的类型肯定是 RxJob
,因为 map
方法是可以连续调用的,
其次,可以确定 map
函数的入参是 Func
,
一个匿名内部类(Java 8
中的 Lambda
表达式),在其他某些语言中就是函数对象、函数引用之类的
由此可以确定 map
方法基本的样子如下代码:
public RxJob map(Func<T,R> func){
...
return rxJob;
};
然后考虑入参 Func<T,R> func
的实现,即这个转变函数该如何实现。
1. 转变函数的确定
首先可以确定这个匿名内部类会实现一个从 T 类型—> R 类型
的转换,
其次 func
对象是在 map 方法调用的时候,才确定具体的实现内容,因此,它必然是一个接口或者抽象方法,待实现
然后,考虑到 T 类型—> R 类型
的转换在具体代码编写的时候是可以确定的,由此考虑可以用泛型方法和泛型类就可以实现。
从而创建 Func<T,R>
接口,该接口有一个 R call(T t)
方法待实现
看如下代码:
public interface Func<T,R> {
/**
*
* @param t transform 的对象类型
* @return 返还的对象类型
*/
R call(T t);
}
该方法返回的类型,是泛型类 Func<T,R>
中的 R
,其入参是泛型类 Func<T,R>
中的 T
即该类的作用就是进行一个从 T —> R
的转换,而具体的转换方法由外部实现。
应用到上面的例子就是,List —> Photo
的转换,即 R call<T t>
方法 变成了 Photo call<List photoList>
于是乎,代码就变成了如下的样子
RxJob queryPhotoJob = new RxJob(..)...;
queryPhotoJob
.map(new Func<List<Photo>,Photo>()
@Overide
Photo call(List<Photo> photoList){
return getBestPhoto(photoList);
})
// 同理第二个 map 就成了如下,
// 入参是上个操作的出参 Photo
// 而出参则是 String 类型的
.map(new Func<Photo,String>(){
@Overide
String call(Photo photo){
return photo.getUrl()//返还 String 类型
}
})
.doJob(...);
至此,map
函数的入参整体已经介绍完毕。
2.doJob
方法的调用
然后考虑里面具体的调用逻辑,首先考虑任务调用,第一个 map
方法在触发的时候,queryPhotoJob
本身已经是一个 RxJob
对象了
它的实现内容也简单,在创建包裹类时已经有解释了。
queryPhotoJob.doJob(new RxCallback<List<Photo>>(){
public void onNext(List<Photo> photoList){..}
public void onError(Exception e){...}
});
也就是说,如果我要调用 map
方法触发传入的 func
匿名内部类,就必须先调用 queryPhotoJob.doJob(..)
于是把代码变成
public RxJob map(Func<T,R> func){
rxJob.doJob(new RxCallback<T>() {
@Override
public void onNext(T t) {
}
@Override
public void onError(Exception e) {
}
});
return ...;
};
应用到上述例子中其具体的泛型值变化就成了如下代码
public RxJob map(Func<List<Photo>,R> func){
rxJob.doJob(new RxCallback<List<Photo>>() {
@Override
public void onNext(List<Photo> photoList) {
}
@Override
public void onError(Exception e) {
}
});
return ...;
};
此时需要考虑的就是在哪里调用 func
函数,它是负责把T —> R
,
因此该部分代码必然是在当前任务执行完后,拿到当前任务的回调值,然后进行转换,即 map
方法会变成如下
public RxJob map(Func<T,R> func){
rxJob.doJob(new RxCallback<T>() {
@Override
public void onNext(T t) {
R mapped = func.call(t);
}
@Override
public void onError(Exception e) {
}
});
return ...;
};
3. map
方法返还值
接下来考虑返回值,首先 map
方法返回的是一个 RxJob
对象
其次,因为当前任务已经执行完了,而下一个任务需要当前任务执行后处理的返回值,即func.call(t)
返回的 mapped
值
如果返回的是原有的 RxJob
那么每次调用 map
方法时执行的代码永远是 this.doJob
因此每次执行的内容都是第一个任务了
应用到此处就是 queryPhotoJob.doJob(..)
。
因此 map
方法返回的值应该是一个新创建的 RxJob
对象,并且该对象的 doJob
方法会先运行当前任务的 doJob
方法
代码如下:
public RxJob map(Func<T,R> func){
RxJob curJob = this;
return new RxJob() {
@Override
public void doJob(final RxCallback rxCallback) {
curJob.doJob(new RxCallback<T>() {
@Override
public void onNext(T t) {
R mapped = func.call(t);
...
}
@Override
public void onError(Exception e) {
...
}
});
}
};
};
把当前例子的泛型值带入如下:
public RxJob map(Func<List<Photo>,R> func){
RxJob queryPhotoJob = this;
RxJob mapJob = new RxJob() {
@Override
public void doJob(final RxCallback rxCallback) {
queryPhotoJob.doJob(new RxCallback<List<Photo>>() {
@Override
public void onNext(List<Photo> photoList) {
R mapped = func.call(photoList);
...
}
@Override
public void onError(Exception e) {
...
}
});
}
};
return mapJob;
};
4. func.call()
的返还值传递
考虑如何把 func.call()
调用后的返回的数据往下一个 RxJob
传递
直接调用下一个 RxJob
中回调的 rxCallback.onNext()
方法即可,对于错误也同样调用 rxCallback.onError(e)
即可。
public RxJob map(Func<T,R> func){
RxJob curJob = this;
return new RxJob() {
@Override
public void doJob(final RxCallback rxCallback) {
curJob.doJob(new RxCallback<T>() {
@Override
public void onNext(T t) {
R mapped = func.call(t);
rxCallback.onNext(t);
}
@Override
public void onError(Exception e) {
rxCallback.onError(e);
}
});
}
};
}
代入当前例子:
public RxJob map(Func<List<Photo>,R> func){
RxJob<List<Photo>> queryPhotoJob = this;
RxJob mapJob = new RxJob() {
@Override
public void doJob(final RxCallback mapJobCallback) {
queryPhotoJob.doJob(new RxCallback<List<Photo>>() {
@Override
public void onNext(List<Photo> photoList) {
R mapped = func.call(photoList);
mapJobCallback.onNext(mapped);
}
@Override
public void onError(Exception e) {
mapJobCallback.onError(e);
}
});
}
};
return mapJob;
}
最终只需要确定泛型参数类型 R
,他的作用是确定下一个RxJob
对象的泛型值
R
由 func.call()
方法 确定,也就是之前说的,在具体的 map
方法被调用时,参数的泛型值是可以确定的
Func
函数负责把 T —> R 转换,因此,最终的 map
方法如下:
public <R> RxJob<R> map(Func<T,R> func){
RxJob curJob = this;
return new RxJob<R>() {
@Override
public void doJob(final RxCallback<R> rxCallback) {
curJob.doJob(new RxCallback<T>() {
@Override
public void onNext(T t) {
R mapped = func.call(t);
rxCallback.onNext(mapped);
}
@Override
public void onError(Exception e) {
rxCallback.onError(e);
}
});
}
};
}
应用到当前例子,带入泛型值如下
public<Photo> RxJob<Photo> map(Func<List<Photo>,Photo> func){
RxJob<List<Photo>> queryPhotoJob = this;
RxJob<Photo> mapJob = new RxJob<Photo>() {
@Override
public void doJob(final RxCallback<Photo> mapJobCallback) {
queryPhotoJob.doJob(new RxCallback<List<Photo>>() {
@Override
public void onNext(List<Photo> photoList) {
Photo mapped = func.call(photoList);
mapJobCallback.onNext(mapped);
}
@Override
public void onError(Exception e) {
mapJobCallback.onError(e);
}
});
}
};
return mapJob;
}
整个任务如何运行
RxJob<List<Photo>> queryPhotoJob = new RxJob<List<Photo>>(){
@Override
public void doJob(final RxCallback<List<Photo>> rxCallback) {
mApi.queryPhoto("flyer", new Api.QueryCallback() {
@Override
public void onQuerySuccess(List<Photo> photoList) {
rxCallback.onNext(photoList);
}
@Override
public void onQueryFailed(Exception e) {
rxCallback.onError(e);
}
});
}
};
};
RxJob<Photo> bestPhotoJob = queryPhotoJob.map(photoList -> { return getBestPhoto(photoList);})
RxJob<String> urlJob = .map(photo -> {return photo.getUrl();})
urlJob.doJob(new RxCallback<String>() {
@Override
public void onNext(String s) {
ToastUtils.show(s);
}
@Override
public void onError(Exception e) {
...
}
});
解释代码,第一个 map
方法 返还了一个 RxJob<Photo>
对象,第二个 map
返还了一个 RxJob<String>
对象,第三个 doJob
方法触发整个任务
doJob
方法触发的第一个方法是 urlJob
中的 doJob
方法,由于 urlJob
对象是由 map
方法创建的
因此,在执行 ToastUtils.show(s)
方法之前,会先执行 bestPhotoJob.doJob()
同样,queryPhotoJob.doJob()
也会执行在 bestPhotoJob.doJob()
方法之前
因此整体过程就是 queryPhotoJob.doJob()
—> bestPhotoJob.doJob()
—> urlJob.doJob()
而这执行过程中,R mapped = func.call(T)
会先被调用,因此单个 RxJob
调用链如下:
在调用每个
RxJob.onNex()
情况下(即成功的情况下,没有出错)
queryPhotoJob.doJob()
: mApi.query
—> func.call(t)
—> getBestPhoto(photoList)
bestPhotoJob.doJob()
: func.call(t)
—> photo.getUrl()
urlJob.doJob()
: ToastUtil.show()
比较炫酷的写法如下:
RxJob<List<Photo>> queryPhotoJob = new RxJob<List<Photo>>(){
@Override
public void doJob(final RxCallback<List<Photo>> rxCallback) {
mApi.queryPhoto("flyer", new Api.QueryCallback() {
@Override
public void onQuerySuccess(List<Photo> photoList) {
rxCallback.onNext(photoList);
}
@Override
public void onQueryFailed(Exception e) {
rxCallback.onError(e);
}
});
}
};
};
queryPhotoJob
.map(photoList -> { return getBestPhoto(photoList);})
.map(photo -> {return photo.getUrl()})
.doJob(...);
=================================
=================================