- 变换操作符的主要开发需求场景 = 嵌套回调(
Callback hell
) - 下面,我将采用一个实际应用场景实例来讲解嵌套回调(
Callback hell
)
1. 需求场景
1.1 背景
需要进行嵌套网络请求:即在第1个网络请求成功后,继续再进行一次网络请求
如 先进行 用户注册 的网络请求, 待注册成功后回再继续发送 用户登录 的网络请求
1.2 冲突
嵌套实现网络请求较为复杂,即嵌套调用函数
下面展示的是结合
Retrofit
与RxJava
的基本用法,即未用操作符前
// 发送注册网络请求的函数方法
private void register() {
api.register(new RegisterRequest())
.subscribeOn(Schedulers.io()) //在IO线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求结果
.subscribe(new Consumer<RegisterResponse>() {
@Override
public void accept(RegisterResponse registerResponse) throws Exception {
Toast.makeText(MainActivity.this, "注册成功", Toast.LENGTH_SHORT).show();
login(); //注册成功, 调用登录的方法
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(MainActivity.this, "注册失败", Toast.LENGTH_SHORT).show();
}
});
}
// 发送登录网络请求的函数方法
private void login() {
api.login(new LoginRequest())
.subscribeOn(Schedulers.io()) //在IO线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求结果
.subscribe(new Consumer<LoginResponse>() {
@Override
public void accept(LoginResponse loginResponse) throws Exception {
Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(MainActivity.this, "登录失败", Toast.LENGTH_SHORT).show();
}
});
}
1.3 解决方案
结合 RxJava2
中的变换操作符FlatMap()
实现嵌套网络请求
2. 功能说明
- 实现功能:发送嵌套网络请求(将英文翻译成中文,翻译两次)
- 为了让大家都能完成
Demo
,所以通过 公共的金山词霸API 来模拟 “注册 - 登录”嵌套网络请求- 即先翻译
Register
(注册),再翻译Login
(登录)
- 实现方案:采用
Get
方法对 金山词霸API 发送网络请求
采用
Gson
进行数据解析
3. 具体实现
下面我将结合 Retrofit
与RxJava
实现网络请求嵌套
3.1 步骤说明
- 添加依赖
- 创建 接收服务器返回数据 的类
- 创建 用于描述网络请求 的接口(区别于
Retrofit
传统形式) - 创建 Retrofit 实例
- 创建 网络请求接口实例 并 配置网络请求参数(区别于
Retrofit
传统形式) - 发送网络请求(区别于
Retrofit
传统形式) - 发送网络请求
- 对返回的数据进行处理
3.2 步骤实现
步骤1: 添加依赖
a. 在 Gradle
加入Retrofit
库的依赖
build.gradle
dependencies {
// Android 支持 Rxjava
// 此处一定要注意使用RxJava2的版本
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
// Android 支持 Retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
// 衔接 Retrofit & RxJava
// 此处一定要注意使用RxJava2的版本
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
// 支持Gson解析
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
}
b. 添加 网络权限
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
步骤2:创建 接收服务器返回数据 的类
- 金山词霸
API
的数据格式说明如下:
// URL模板
http://fy.iciba.com/ajax.php
// URL实例
http://fy.iciba.com/ajax.php?a=fy&f=auto&t=auto&w=hello%20world
// 参数说明:
// a:固定值 fy
// f:原文内容类型,日语取 ja,中文取 zh,英语取 en,韩语取 ko,德语取 de,西班牙语取 es,法语取 fr,自动则取 auto
// t:译文内容类型,日语取 ja,中文取 zh,英语取 en,韩语取 ko,德语取 de,西班牙语取 es,法语取 fr,自动则取 auto
// w:查询内容
- 示例
- 根据 金山词霸API 的数据格式,创建 接收服务器返回数据 的类:
为了演示是2个网络请求,所以对应设置2个接收服务器的数据类
<-- Translation1.java -->
public class Translation1 {
private int status;
private content content;
private static class content {
private String from;
private String to;
private String vendor;
private String out;
private int errNo;
}
//定义 输出返回数据 的方法
public void show() {
Log.d("RxJava", "翻译内容 = " + content.out);
}
}
<-- Translation2.java -->
public class Translation2 {
private int status;
private content content;
private static class content {
private String from;
private String to;
private String vendor;
private String out;
private int errNo;
}
//定义 输出返回数据 的方法
public void show() {
Log.d("RxJava", "翻译内容 = " + content.out);
}
}
步骤3:创建 用于描述网络请求 的接口
采用 注解 + Observable<...>
接口描述 网络请求参数
GetRequest_Interface.java
public interface GetRequest_Interface {
// 网络请求1
@GET("ajax.php?a=fy&f=auto&t=auto&w=hi%20register")
Observable<Translation1> getCall();
// 网络请求2
@GET("ajax.php?a=fy&f=auto&t=auto&w=hi%20login")
Observable<Translation2> getCall_2();
// 注解里传入 网络请求 的部分URL地址
// Retrofit把网络请求的URL分成了两部分:一部分放在Retrofit对象里,另一部分放在网络请求接口里
// 如果接口里的url是一个完整的网址,那么放在Retrofit对象里的URL可以忽略
// 采用Observable<...>接口
// getCall()是接受网络请求数据的方法
}
接下来的步骤均在MainActivity.java内实现(请看注释)
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Rxjava";
// 定义Observable接口类型的网络请求对象
Observable<Translation1> observable1;
Observable<Translation2> observable2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 步骤1:创建Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://fy.iciba.com/") // 设置 网络请求 Url
.addConverterFactory(GsonConverterFactory.create()) //设置使用Gson解析(记得加入依赖)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 支持RxJava
.build();
// 步骤2:创建 网络请求接口 的实例
GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
// 步骤3:采用Observable<...>形式 对 2个网络请求 进行封装
observable1 = request.getCall();
observable2 = request.getCall_2();
observable1.subscribeOn(Schedulers.io()) // (初始被观察者)切换到IO线程进行网络请求1
.observeOn(AndroidSchedulers.mainThread()) // (新观察者)切换到主线程 处理网络请求1的结果
.doOnNext(new Consumer<Translation1>() {
@Override
public void accept(Translation1 result) throws Exception {
Log.d(TAG, "第1次网络请求成功");
result.show();
// 对第1次网络请求返回的结果进行操作 = 显示翻译结果
}
})
.observeOn(Schedulers.io()) // (新被观察者,同时也是新观察者)切换到IO线程去发起登录请求
// 特别注意:因为flatMap是对初始被观察者作变换,所以对于旧被观察者,它是新观察者,所以通过observeOn切换线程
// 但对于初始观察者,它则是新的被观察者
.flatMap(new Function<Translation1, ObservableSource<Translation2>>() { // 作变换,即作嵌套网络请求
@Override
public ObservableSource<Translation2> apply(Translation1 result) throws Exception {
// 将网络请求1转换成网络请求2,即发送网络请求2
return observable2;
}
})
.observeOn(AndroidSchedulers.mainThread()) // (初始观察者)切换到主线程 处理网络请求2的结果
.subscribe(new Consumer<Translation2>() {
@Override
public void accept(Translation2 result) throws Exception {
Log.d(TAG, "第2次网络请求成功");
result.show();
// 对第2次网络请求返回的结果进行操作 = 显示翻译结果
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
System.out.println("登录失败");
}
});
}
}
3.3 测试结果