用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 ...
随机推荐
- SQL与NoSQL(关系型与非关系型)数据库的区别
永远正确的经典答案依然是:具体问题具体分析. 数据表VS.数据集 关系型和非关系型数据库的主要差异是数据存储的方式.关系型数据天然就是表格式的,因此存储在数据表的行和列中.数据表可以彼此关联协作存储, ...
- Python运算符,python入门到精通[五]
运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算.例如:2+3,其操作数是2和3,而运算符则是“+”.在计算器语言中运算符大致可以分为5种类型:算术运算符.连接运算符.关系运算符.赋值运 ...
- windows环境下无法引用全局安装的模块问题
问题 在node项目中,往往需要安装一些依赖的包,通常我们采取全局安装的方式,来减少一些包重复安装带来的烦恼. 但是全局安装后出现无法使用的情况,可能是你NODE_PATH没有设置或者不正确造成的. ...
- C#邮件发送问题(一)
邮件发送需考虑很多因素,包括发送邮件客户端(一般编码实现),发送和接收邮件服务器设置等.如果使用第三方邮件服务器作为发送服务器,就需要考虑该服务器的发送限制,(如发送邮件时间间隔,单位时间内发送邮件数 ...
- 搭建持续集成接口测试平台(Jenkins+Ant+Jmeter)
一.环境准备: 1.JDK:http://www.oracle.com/technetwork/java/javase/downloads/index.html 2.Jmeter:http://jme ...
- linux centos使用xrdp远程界面登陆
redhat6 安装xrdp 直接使用windows远程桌面连接登陆 下面介绍实现方法: 第一步:下载源码包,并安装一些依赖的软件下载xrdp源码包 wget http://downloads.so ...
- Java实现点击一个控件实现删除一个控件的方法
最近在做项目的时候需要处理点击一个JLabel实现删除这一个JLabel的功能.最近折磨了一点时间,查了一下API.找到2个方法可以实现这个功能. remove public void remove( ...
- openjudge6047分蛋糕[DP]
描述 有一块矩形大蛋糕,长和宽分别是整数w .h.现要将其切成m块小蛋糕,每个小蛋糕都必须是矩形.且长和宽均为整数.切蛋糕时,每次切一块蛋糕,将其分成两个矩形蛋糕.请计算:最后得到的m块小蛋糕中,最大 ...
- 第8章 用户模式下的线程同步(3)_Slim读写锁(SRWLock)
8.5 Slim读/写锁(SRWLock)——轻量级的读写锁 (1)SRWLock锁的目的 ①允许读者线程同一时刻访问共享资源(因为不存在破坏数据的风险) ②写者线程应独占资源的访问权,任何其他线程( ...
- AC日记——元素查找 codevs 1230
1230 元素查找 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 查看运行结果 题目描述 Description 给出n个正整数,然后有 ...