用RxJava处理嵌套请求
用RxJava处理嵌套请求
互联网应用开发中由于请求网络数据频繁,往往后面一个请求的参数是前面一个请求的结果,于是经常需要在前面一个请求的响应中去发送第二个请求,从而造成“请求嵌套”的问题。如果层次比较多,代码可读性和效率都是问题。本文首先从感性上介绍下RxJava,然后讲解如何通过RxJava中的flatMap操作符来处理“嵌套请求”的问题
内容提要
- RxJava简单介绍
- 嵌套请求举例
- 运用flatMap
- map和flatMap
- RxJava与Retrofit配合解决嵌套请求
RxJava简单介绍
这里并不打算详细介绍RxJava的用法和原理,这方面的文章已经很多了。这里仅仅阐述本人对于RxJava的感性上的理解。先上一个图:

我们都知道RxJava是基于观察者模式的,但是和传统的观察者模式又有很大的区别。传统的观察者模式更注重订阅和发布这个动作,而RxJava的重点在于数据的“流动”。
如果我们把RxJava中的Observable看做一个盒子,那么Observable就是把数据或者事件给装进了这个易于拿取的盒子里面,让订阅者(或者下一级别的盒子)可以拿到而处理。这样一来,原来静态的数据/事件就被流动起来了。
我们知道人类会在河流中建设大坝,其实我们可以把RxJava中的filter/map/merge等Oberservable操作符看做成数据流中的大坝,经过这个操作符的操作后,大坝数据流被过滤被合并被处理,从而灵活的对数据的流动进行管制,让最终的使用者灵活的拿到。
以上就是我对RxJava的理解,深入的用法和原理大家请自行看网上的文章。
嵌套请求举例
这里开始进入正题,开始举一个嵌套请求的例子。
比如我们下列接口:
- api/students/getAll (传入班级的id获得班级的学生数组,返回值是list)
- api/courses/getAll (传入Student的id获得这个学生所上的课程,返回值是List)
我们最终的目的是要打印班上所有同学分别所上的课程(大学,同班级每个学生选上的课不一样),按照传统Volley的做法,代码大概是这样子(Volley已经被封装过)
private void getAllStudents(String id) {
BaseRequest baseRequest = new BaseRequest();
baseRequest.setClassId(id);
String url = AppConfig.SERVER_URL + "api/students/getAll";
final GsonRequest request = new GsonRequest<>(url, baseRequest, Response.class, new Response.Listener<Response>() {
@Override
public void onResponse(Response response) {
if (response.getStatus() > 0) {
List<Student> studentList = response.getData();
for (Student student : studentList) {
}
} else {
//error
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//error
}
});
MyVolley.startRequest(request);
}
private void getAllCourses(String id) {
BaseRequest baseRequest = new BaseRequest();
baseRequest.setStudentId(id);
String url = AppConfig.SERVER_URL + "api/courses/getAll";
final GsonRequest request = new GsonRequest<>(url, baseRequest, Response.class, new Response.Listener<Response>() {
@Override
public void onResponse(Response response) {
if (response.getStatus() > 0) {
List<Course> courseList = response.getData();
for (Course course : courseList) {
//use
}
} else {
//error
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//error
}
});
MyVolley.startRequest(request);
}
显然第一个请求的响应中获得的数据是一个List,正对每一个List中的item要再次发送第二个请求,在第二个请求中获得最终的结果。这就是一个嵌套请求。这会有两个问题:
- 目前来看并不复杂,如果嵌套层次多了,会造成代码越来越混乱
- 写出来的重复代码太多
运用flatMap
现在我们可以放出RxJava大法了,flatMap是一个Observable的操作符,接受一个Func1闭包,这个闭包的第一个函数是待操作的上一个数据流中的数据类型,第二个是这个flatMap操作完成后返回的数据类型的被封装的Observable。说白了就是讲一个多级数列“拍扁”成了一个一级数列。
按照上面的列子,flatMap将接受student后然后获取course的这个二维过程给线性化了,变成了一个可观测的连续的数据流。
于是代码是:
ConnectionBase.getApiService2()
.getStudents(101)
.flatMap(new Func1<Student, Observable<Course>>() {
@Override
public Observable<Course> call(Student student) {
return ConnectionBase.getApiService2().getAllCourse(student.getId());
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Course>() {
@Override
public void call(Course course) {
//use the Course
}
});
是不是代码简洁的让你看不懂了?别急,这里面的getStutent和getAllCourse是ConnectionBase.getApiService2()的两个方法,他集成了Retrofit2用来将请求的网络数据转化成Observable,最后一节将介绍,这里先不关注。
我们所要关注的是以上代码的流程。
首先getStudent传入了班级id(101)返回了Observable,然后链式调用flatMap操作符对这个Observable进行变换处理,针对每一个发射出来的Student进行再次请求 ConnectionBase.getApiService2().getAllCourse从而返回Observable,最后对这个 ConnectionBase.getApiService2().getAllCourse进行订阅,即subscribe方法,再Action1这个闭包的回调中使用course。
flatMap的作用就是对传入的对象进行处理,返回下一级所要的对象的Observable包装。
FuncX和ActionX的区别。FuncX包装的是有返回值的方法,用于Observable的变换、组合等等;ActionX用于包装无返回值的方法,用于subscribe方法的闭包参数。Func1有两个入参,前者是原始的参数类型,后者是返回值类型;而Action1只有一个入参,就是传入的被消费的数据类型。
subscribeOn(Schedulers.io()).observeOn(AndroidScheduler.mainThread())是最常用的方式,后台读取数据,主线程更新界面。subScribeOn指在哪个线程发射数据,observeOn是指在哪里消费数据。由于最终的Course要刷新界面,必须要在主线程上调用更新view的方法,所以observeOn(AndroidScheduler.mainThread())是至关重要的。
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);
}
}
});
}
});
RxJava与Retrofit配合解决嵌套请求
这里该讨论Retrofit了。可以说Retrofit就是为了RxJava而生的。如果你的项目之前在网络请求框架用的是Volley或者自己封装Http请求和TCP/IP,而现在你看到了Retrofit这个框架后想使用起来,我可以负责任的跟你说,如果你的项目中没有使用RxJava的话,使用Retrofit和Volley是没有区别的!要用Retrofit的话,就最好或者说强烈建议也使用RxJava进行编程。
Retrofit有callback和Observable两种模式,前者就像传统的Volley一样,有successs和fail的回调方法,我们在success回调方法中处理结果;而Observable模式是将请求回来的数据由Retrofit框架自动的帮你加了一个盒子,即自动帮你装配成了含有这个数据的Observable,供你使用RxJava的操作符随意灵活的进行变换。
callback模式的Retrofit是这样建立的:
retrofit = new Retrofit.Builder()
.baseUrl(SERVER_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
Observable模式是这样子建立的:
retrofit2 = new Retrofit.Builder()
.baseUrl(SERVER_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
即addCallAdapterFactory这个方法在起作用,在RxJavaCallAdapterFactory的源码注释中可以看到这么一句话:
Response wrapped body (e.g., {@code Observable<Response>}) calls {@code onNext} with a {@link Response} object for all HTTP responses and calls {@code onError} with {@link IOException} for network errors
即它将返回值body为包裹上了一层“Observable”
用RxJava处理嵌套请求的更多相关文章
- kotlin for android----------MVP模式下(OKHttp和 Retrofit+RxJava)网络请求的两种实现方式
今天要说的干货是:以Kotlin,在MVP模式下(OKHttp和 Retrofit+RxJava)网络请求两种实现方式的一个小案例,希望对大家有所帮助,效果图: Retrofit是Square公司开发 ...
- 使用 RxJava 进行嵌套串行网络请求的一种方法
需求 有这样一个列表数据,它包含了商店+订单的信息,获取订单列表时,订单实体中会包含商店的 ID,而列表显示时需要商店的名称和 logo,这时候就需要进行嵌套串行网络请求了. 关键词 flatMap ...
- 一步步搭建Retrofit+RxJava+MVP网络请求框架(一)
首先,展示一下封装好之后的项目的层级结构. 1.先创建一个RetrofitApiService.java package com.xdw.retrofitrxmvpdemo.http; import ...
- 一步步搭建Retrofit+RxJava+MVP网络请求框架(二),个人认为这次封装比较强大了
在前面已经初步封装了一个MVP的网络请求框架,那只是个雏形,还有很多功能不完善,现在进一步进行封装.添加了网络请求时的等待框,retrofit中添加了日志打印拦截器,添加了token拦截器,并且对Da ...
- android开发学习 ------- Retrofit+Rxjava+MVP网络请求的实例
http://www.jianshu.com/p/7b839b7c5884 推荐 ,照着这个敲完 , 测试成功 , 推荐大家都去看一下 . 下面贴一下我照着这个敲完的代码: Book实体类 - 用 ...
- Retrofit+Okhttp+RxJava打造网络请求之Post
之前一直在准备Android培训的事情,所幸的是终于完事啦,在这过程中真的发现了自身无论从沟通能力还是技术能力上很多的不足,就用一句 路漫漫其修远兮,吾将上下而求索 来勉励自己吧.之前也在项目上用上O ...
- retrofit2+rxjava+okhttp网络请求实现
第一步:添加依赖: compile 'io.reactivex:rxandroid:1.2.0' compile 'com.squareup.retrofit2:adapter-rxjava:2.1. ...
- 基于Retrofit+RxJava的Android分层网络请求框架
目前已经有不少Android客户端在使用Retrofit+RxJava实现网络请求了,相比于xUtils,Volley等网络访问框架,其具有网络访问效率高(基于OkHttp).内存占用少.代码量小以及 ...
- Retrofit结合RxJava使用指南
Retrofit结合RxJava使用指南 Retrofit是一个当前很流行的网络请求库, 官网的介绍是: "Type-safe HTTP client for Android and Jav ...
随机推荐
- coursera机器学习笔记-多元线性回归,normal equation
#对coursera上Andrew Ng老师开的机器学习课程的笔记和心得: #注:此笔记是我自己认为本节课里比较重要.难理解或容易忘记的内容并做了些补充,并非是课堂详细笔记和要点: #标记为<补 ...
- dubbo-admin-2.5.3 运行报错: Bean property 'URIType' is not writable or has an invalid 解决方法
因为 jdk 是1.8的版本,和 dubbo-admin 存在兼容性问题.所以报错: Bean property 'URIType' is not writable or has an invalid ...
- Tomcat 目录详解
一.文字版本: bin: 该目录下存放的是二进制可执行文件,如果是安装版,那么这个目录下会有两个exe文件:tomcat6.exe.tomcat6w.exe,前者是在控制台下启动Tomcat,后者是 ...
- linux 源码安装mysql 5.5
今天在ubuntu和CentOS下,用源码反复安装了许多次mysql,趁还没忘记,赶紧记下来... 在ubuntu和CentOS下安装过程倒是没什么差别. 0.下载源码, ...
- BUCK-BOOST反激变压器设计
Buck-Boost电路中,最低电压为其最恶劣情况 以下图为例: 注:1.Np为初级绕组匝数,Ns为次级绕组匝数: 2.Vmos为MOS最大耐压值,1为整流管压降,Vl为漏,Vl=100V,Vmos选 ...
- java utf-8文件处理bom头
UTF? UTF,是UnicodeTransformationFormat的缩写,意为Unicode转换格式. 即怎样将Unicode定义的数字转换成程序数据.utf是对Unicode的一种编码格式化 ...
- 以纯面向对象的JS编写最基本的数据字典案例
之前有讲到过数据字典,什么是数据字典,用来干啥的,这个不细说了,今天来说说如何实现数据字典功能 无非就是维护数据字典,对数据字典对象进行增删改成,曾经我写过一个页面跳转形式的,十分简单,不说了,今天用 ...
- 实例变量和静态变量(或类变量static)
一个类通过使用运算符new可以创建多个不同的对象,这些对象将被分配不同的内存空间,准确的说法是:不同对象的实例变量将被分配不同的内存空间,如果类中有类变量,那么所有对象的这个类变量都被分配到同一处内存 ...
- Could not load file or assembly 'MySql.Data.CF,
Could not load file or assembly 'MySql.Data.CF, Version=6.4.4.0, Culture=neutral, PublicKeyToken=c56 ...
- java 24 - 11 GUI之制作登陆注册页面
简单说说,懒得发了... 步骤: A:首先写出登陆注册需要用到类以及代码(IO流) B:然后创建登陆窗口和注册窗口 C:各个监听事件: a:登录窗口 1.重置:把2个文本框的内容全部清空 2.注册:关 ...