Android RxJava 应用讲解:网络请求出错重连(结合Retrofit)

2,330 阅读7分钟
原文链接: www.jianshu.com

前言

  • Rxjava,由于其基于事件流的链式调用、逻辑简洁 & 使用简单的特点,深受各大 Android开发者的欢迎。
Github截图
Github截图

如果还不了解RxJava,请看文章:Android:这是一篇 清晰 & 易懂的Rxjava 入门教程

  • RxJava如此受欢迎的原因,在于其提供了丰富 & 功能强大的操作符,几乎能完成所有的功能需求
  • 今天,我将为大家带来 Rxjava创建操作符的实际开发需求场景:网络请求出错重连需求 ,并结合RetrofitRxJava 实现,希望大家会喜欢。
  1. 本系列文章主要基于 Rxjava 2.0
  2. 接下来的时间,我将持续推出 AndroidRxjava 2.0 的一系列文章,包括原理、操作符、应用场景、背压等等 ,有兴趣可以继续关注Carson_Ho的安卓开发笔记!!
示意图
示意图

目录

示意图
示意图

1. 需求场景

示意图
示意图

2. 功能说明

  • 功能需求说明
示意图
示意图

注:关于 Rxjava中的retryWhen() 操作符的使用请看文章Android RxJava:功能性操作符 全面讲解

  • 功能逻辑
示意图
示意图
  • 实例说明
    在本例子中:采用Get方法对 金山词霸API 发送网络请求
    1. 通过 断开网络连接 模拟 网络异常错误(恢复网络即可成功发送请求)
    2. 限制重试次数 = 10次
    3. 采用 Gson 进行数据解析
金山词典
金山词典

3. 具体实现

下面,我将结合 RetrofitRxJava 实现 网络请求出错重连 功能

3.1 步骤说明

  1. 添加依赖
  2. 创建 接收服务器返回数据 的类
  3. 创建 用于描述网络请求 的接口(区别于Retrofit传统形式)
  4. 创建 Retrofit 实例
  5. 创建 网络请求接口实例 并 配置网络请求参数(区别于Retrofit传统形式)
  6. 发送网络请求(区别于Retrofit传统形式)
  7. 发送网络请求
  8. 对返回的数据进行处理

下面,将进行详细的功能说明。

本实例侧重于说明 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:创建 接收服务器返回数据 的类
  • 金山词霸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格式说明
API格式说明
  • 根据 金山词霸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 测试结果

  1. 一开始先通过 断开网络连接 模拟 网络异常错误,即开始重试;
  2. 等到第3次重试后恢复网络连接,即无发生网络异常错误,此时重试成功
示意图
示意图

4. Demo地址

Carson_Ho的Github地址 = RxJava2实战系列:出错重连


5. 总结

  • 本文主要讲解了 Rxjava创建操作符的实际开发需求场景:网络请求出错重连需求 ,并结合RetrofitRxJava 实现
  • 下面我将结合 实际场景应用 & Rxjava的相关使用框架(如RetrofitEventbus ,继续对 AndroidRxjava 的实际开发需求场景进行深入讲解 ,有兴趣可以继续关注Carson_Ho的安卓开发笔记

请点赞!因为你的鼓励是我写作的最大动力!

相关文章阅读


欢迎关注Carson_Ho的简书!

不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度