正文
前言
-
Rxjava
,由于其
基于事件流的链式调用、逻辑简洁 & 使用简单
的特点,深受各大
Android
开发者的欢迎。
如果还不了解RxJava,请看文章:
Android:这是一篇 清晰 & 易懂的Rxjava 入门教程
-
RxJava
如此受欢迎的原因,在于其
提供了丰富 & 功能强大的操作符,几乎能完成所有的功能需求
-
今天,我将为大家带来
Rxjava
创建操作符的
实际开发需求场景:网络请求出错重连需求
,并结合
Retrofit
与
RxJava
实现,希望大家会喜欢。
-
本系列文章主要基于
Rxjava 2.0
-
接下来的时间,
我将持续推出
Android
中
Rxjava 2.0
的一系列文章,包括原理、操作符、应用场景、背压等等
,有兴趣可以继续关注
Carson_Ho的安卓开发笔记
!!
目录
1. 需求场景
2. 功能说明
注:关于
Rxjava
中的
retryWhen()
操作符的使用请看文章
Android RxJava:功能性操作符 全面讲解
-
实例说明
在本例子中:采用
Get
方法对 金山词霸API 发送网络请求
-
通过 断开网络连接 模拟
网络异常错误
(恢复网络即可成功发送请求)
-
限制重试次数 = 10次
-
采用
Gson
进行数据解析
3. 具体实现
下面,我将结合
Retrofit
与
RxJava
实现 网络请求出错重连 功能
3.1 步骤说明
-
添加依赖
-
创建 接收服务器返回数据 的类
-
创建 用于描述网络请求 的接口(区别于
Retrofit
传统形式)
-
创建
Retrofit
实例
-
创建 网络请求接口实例 并 配置网络请求参数(区别于
Retrofit
传统形式)
-
发送网络请求(区别于
Retrofit
传统形式)
-
发送网络请求
-
对返回的数据进行处理
下面,将进行详细的功能说明。
本实例侧重于说明
RxJava
的轮询需求,关于
Retrofit
的使用请看文章:
这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)
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:创建 接收服务器返回数据 的类
// 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 的数据格式,创建 接收服务器返回数据 的类:
Translation.java
public class Translation {
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 {
@GET("ajax.php?a=fy&f=auto&t=auto&w=hi%20world")
Observable<Translation> getCall();
// 注解里传入 网络请求 的部分URL地址
// Retrofit把网络请求的URL分成了两部分:一部分放在Retrofit对象里,另一部分放在网络请求接口里
// 如果接口里的url是一个完整的网址,那么放在Retrofit对象里的URL可以忽略
// 采用Observable<...>接口
// getCall()是接受网络请求数据的方法
}
接下来的步骤均在
RxJavafixRxjava.java
内实现(请看注释)
RxJavafixRxjava.java
public class RxJavafixRetrofit2 extends AppCompatActivity {
private static final String TAG = "RxJava";
// 设置变量
// 可重试次数
private int maxConnectCount = 10;
// 当前已重试次数
private int currentRetryCount = 0;
// 重试等待时间
private int waitRetryTime = 0;
@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<...>形式 对 网络请求 进行封装
Observable<Translation> observable = request.getCall();
// 步骤4:发送网络请求 & 通过retryWhen()进行重试
// 注:主要异常才会回调retryWhen()进行重试
observable.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Observable<Throwable> throwableObservable) throws Exception {
// 参数Observable<Throwable>中的泛型 = 上游操作符抛出的异常,可通过该条件来判断异常的类型
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {
// 输出异常信息
Log.d(TAG, "发生异常 = "+ throwable.toString());
/**
* 需求1:根据异常类型选择是否重试
* 即,当发生的异常 = 网络异常 = IO异常 才选择重试
*/
if (throwable instanceof IOException){
Log.d(TAG, "属于IO异常,需重试" );
/**
* 需求2:限制重试次数
* 即,当已重试次数 < 设置的重试次数,才选择重试
*/
if (currentRetryCount < maxConnectCount){
// 记录重试次数
currentRetryCount++;
Log.d(TAG, "重试次数 = " + currentRetryCount);
/**
* 需求2:实现重试
* 通过返回的Observable发送的事件 = Next事件,从而使得retryWhen()重订阅,最终实现重试功能
*
* 需求3:延迟1段时间再重试
* 采用delay操作符 = 延迟一段时间发送,以实现重试间隔设置
*
* 需求4:遇到的异常越多,时间越长
* 在delay操作符的等待时间内设置 = 每重试1次,增多延迟重试时间1s
*/
// 设置等待时间
waitRetryTime = 1000 + currentRetryCount* 1000;
Log.d(TAG, "等待时间 =" + waitRetryTime);
return Observable.just(1).delay(waitRetryTime, TimeUnit.MILLISECONDS);
}else{
// 若重试次数已 > 设置重试次数,则不重试
// 通过发送error来停止重试(可在观察者的onError()中获取信息)
return Observable.error(new Throwable("重试次数已超过设置次数 = " +currentRetryCount + ",即 不再重试"));
}
}
// 若发生的异常不属于I/O异常,则不重试
// 通过返回的Observable发送的事件 = Error事件 实现(可在观察者的onError()中获取信息)
else{
return Observable.error(new Throwable("发生了非网络异常(非I/O异常)"));
}
}
});
}
}).subscribeOn(Schedulers.io()) // 切换到IO线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) // 切换回到主线程 处理请求结果
.subscribe(new Observer<Translation>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Translation result) {
// 接收服务器返回的数据
Log.d(TAG, "发送成功");
result.show();
}
@Override
public void onError(Throwable e) {
// 获取停止重试的信息
Log.d(TAG, e.toString());
}
@Override
public void onComplete() {
}
});
}
}
3.3 测试结果
-
一开始先通过 断开网络连接 模拟
网络异常错误
,即开始重试;
-
等到第3次重试后恢复网络连接,即无发生网络异常错误,此时重试成功