map和flatMap
运用flatMap的地方也是可以用map的,但是是有区别的。先看下map操作符的用法:
ConnectionBase.getApiService2()
.getStudents(101)
.map(new Func1<Student>, Course>() {
@Override
public Course call(Student student) {
return conventStudentToCourse();// has problem
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Course>() {
@Override
public void call(Course course) {
//use the Course
}
});
可以看到map也是接受一个Func1闭包,但是这个闭包的第二个参数即返回值参数类型并不是一个被包装的Observable,而是实际的原始类型,由于call的返回值是Course,所以conventStudentToCourse这里就不能用Retrofit2的方式返回一个Observable了。
所以这里是有一个问题的,对于这种嵌套的网络请求,由于接到上端数据流到处理后将结果数据放入下端数据流是一个异步的过程,而conventStudentToCourse这种直接将Student转化为Course是没法做到异步的,因为没有回调方法。那么这种情况,最好还是用flatMap并通过retrofit的方式来获取Observable。要知道,Rxjava的一个精髓就是“异步”。
那么到底map和flatMap有什么区别,或者说什么时候使用map什么时候使用flatMap呢?
flatMap() 和 map() 有一个相同点:它也是把传入的参数转化之后返回另一个对象。但需要注意,和 map() 不同的是, flatMap() 中返回的是个 Observable 对象,并且这个 Observable 对象并不是被直接发送到了 Subscriber 的回调方法中。
首先,如果你需要将一个类型的对象经过处理(非异步)直接转化成下一个类型,推荐用map,否则的话就用flatMap。
其次,如果你需要在处理中加入容错的机制(特别是你自己封装基于RxJava的网络请求框架),推荐用flatMap。
比如将一个File[] jsonFile中每个File转换成String,用map的话代码为:
Observable.from(jsonFile).map(new Func1<File, String>() {
@Override public String call(File file) {
try {
return new Gson().toJson(new FileReader(file), Object.class);
} catch (FileNotFoundException e) {
// So Exception. What to do ?
}
return null; // Not good :(
}
});
可以看到这里在出现错误的时候直接抛出异常,这样的处理其实并不好,特别如果你自己封装框架,这个异常不大好去抓取。
如果用flatMap,由于flatMap的闭包返回值是一个Observable,所以我们可以在这个闭包的call中通过Observable.create的方式来创建Observable,而要知道create方法是可以控制数据流下端的Subscriber的,即可以调用onNext/onCompete/onError方法。如果出现异常,我们直接调用subscribe.onError即可,封装框架也很好感知。代码大致如下:
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
@Override public Observable<String> call(final File file) {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override public void call(Subscriber<? super String> subscriber) {
try {
String json = new Gson().toJson(new FileReader(file), Object.class);
subscriber.onNext(json);
subscriber.onCompleted();
} catch (FileNotFoundException e) {
subscriber.onError(e);
}
}
});
}
});