【Android】RxJava2+Retrofit2+OkHttp3的基础、封装和项目中的使用

前言:

近些年很火的Retrofit+RxJava+OkHttp网络请求框架,功能强大,结构合理,使用简单方便。后面还会给大家发自己整理过的Retrofit和RxJava、RxAndroid和RxBus。希望大家点一下关注,让我这个懒癌患者有动力继续写下去!
本篇分三个部分:基础篇、封装篇和自己项目使用篇,项目是自己公司的APP提取的,文章偏长可以分三部分一点点看,当初看了很多优秀的文章然后自己在整理写在印象笔记中,可惜当初没记下借鉴过的文章地址,十分抱歉。

首先这篇文章是参考了@God丶Eye的改写的,地址:https://www.jianshu.com/p/0ad99e598dba,本文稍作修改。
他的demo Github地址:https://github.com/bigeyechou/NetWorkFrame

简单介绍Retrofit、OKHttp和RxJava之间的关系:

  • Retrofit:Retrofit是Square公司开发的一款针对Android 网络请求的框架(底层默认是基于OkHttp 实现)。
  • OkHttp:也是Square公司的一款开源的网络请求库。
  • RxJava :"a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)。RxJava使异步操作变得非常简单。

各自职责:Retrofit 负责 请求的数据 和 请求的结果,使用 接口的方式 呈现,OkHttp 负责请求的过程,RxJava 负责异步,各种线程之间的切换。

一步一步深入:

1、引入Retrofit的包,在build.gradle文件中添加如下配置:

    implementation 'com.google.code.gson:gson:2.6.2'
    implementation 'com.alibaba:fastjson:1.2.9'
    //导入Gson 库
    //导入RxJava 和 RxAndroid
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
    implementation 'io.reactivex.rxjava2:rxjava:2.0.5'
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    //导入retrofit
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    //转换器,请求结果转换成Model
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    //配合Rxjava 使用
    implementation 'com.squareup.okhttp3:logging-interceptor:3.1.2'
    //添加HttpLoggingInterceptor进行调试
    //butterknife不是必须
    implementation 'com.jakewharton:butterknife:7.0.1'
    implementation 'com.orhanobut:logger:1.15'

2、创建OkHttpClient.Builder 实例,并且完成相关的配置(包含cache、header、timeout....):

//手动创建一个OkHttpClient并设置超时时间
        okHttpBuilder = new OkHttpClient.Builder();
/**
         * 设置缓存
         */
        File cacheFile = new File(BigEyeApplication.appContext.getExternalCacheDir(), CACHE_NAME);
        Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
        Interceptor cacheInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();
                if (!NetUtil.isNetworkConnected()) {
                    request = request.newBuilder()
                            .cacheControl(CacheControl.FORCE_CACHE)
                            .build();
                }
                Response response = chain.proceed(request);
                if (!NetUtil.isNetworkConnected()) {
                    int maxAge = 0;
                    // 有网络时 设置缓存超时时间0个小时
                    response.newBuilder()
                            .header("Cache-Control", "public, max-age=" + maxAge)
                            .removeHeader(CACHE_NAME)// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
                            .build();
                } else {
                    // 无网络时,设置超时为4周
                    int maxStale = 60 * 60 * 24 * 28;
                    response.newBuilder()
                            .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                            .removeHeader(CACHE_NAME)
                            .build();
                }
                return response;
            }
        };
        okHttpBuilder.cache(cache).addInterceptor(cacheInterceptor);


        /**
         * 设置头信息
         */
        Interceptor headerInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request originalRequest = chain.request();
                Request.Builder requestBuilder = originalRequest.newBuilder()
                        .addHeader("Accept-Encoding", "gzip")
                        .addHeader("Accept", "application/json")
                        .addHeader("Content-Type", "application/json; charset=utf-8")
                        .method(originalRequest.method(), originalRequest.body());
                requestBuilder.addHeader("Authorization", "Bearer " + BaseConstant.TOKEN);//添加请求头信息,服务器进行token有效性验证
                Request request = requestBuilder.build();
                return chain.proceed(request);
            }
        };
        okHttpBuilder.addInterceptor(headerInterceptor);


//        if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                Logger.d(message);
            }


        });
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        //设置 Debug Log 模式
        okHttpBuilder.addInterceptor(loggingInterceptor);
//        }

        /**
         * 设置超时和重新连接
         */
        okHttpBuilder.connectTimeout(DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS);
        okHttpBuilder.readTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.SECONDS);
        okHttpBuilder.writeTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS);
        //错误重连
        okHttpBuilder.retryOnConnectionFailure(true);

3、创建一个Retrofit 实例,并且完成相关的配置:
配置了接口的 BASE_URL 和一个 converter , GsonConverterFactory 是默认提供的 Gson转换器。

retrofit = new Retrofit.Builder()
                .client(okHttpBuilder.build())
                .addConverterFactory(GsonConverterFactory.create())//json转换成JavaBean
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();

4、创建接口,当然这些接口可以全部封装在一个类里面:

httpApi = retrofit.create(HttpApi.class);

HttpApi类为接口封装类,配合RxJava 使用,更改定义的接口,返回值不再是一个 Call ,而是返回的一个 Observble:

    //请填写自己的接口名
    @GET("api/XXX/GetDoubanMovieBean")
    Observable<ResponseBody> getDataForMap(@QueryMap Map<String, Integer> map);

    /**
     * 通过地址下载一个文件
     */
    @GET()
    @Streaming
    Call<ResponseBody> downloadFile(@Url String fileUrl);

5、加入RxJava的好处:

  • 加入 RxJava 后的网络请求,返回不再是一个 Call ,而是一个 Observable。
  • 在Activity / Fragment 中传入一个Subscriber 建立订阅关系,就可以在 onNext 中处理结果了。
  • RxJava 的好处是帮我处理 线程之间的切换,我们可以在指定 订阅的在哪个线程,观察在哪个线程。
  • 可以 通过操作符 进行数据变换。
  • 整个过程都是链式的,简化逻辑。其中FlatMap 操作符 还可以解除多层嵌套的问题。

RxJava 很强大,能帮我处理很多复杂的场景,如果熟练使用的话,那么能提升我们的开发效率。

6、自定义一个DisposableObserver与Activity 或者 Fragment 建立订阅关系:

public class OnSuccessAndFaultSub extends DisposableObserver<ResponseBody>
        implements ProgressCancelListener {
    /**
     * 是否需要显示默认Loading
     */
    private boolean showProgress = true;
    private OnSuccessAndFaultListener mOnSuccessAndFaultListener;

    private Context context;
    private ProgressDialog progressDialog;

    private Class<?> mClass;

    /**
     * @param mOnSuccessAndFaultListener 成功回调监听
     */
    public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Class<?> tClass) {
        this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
        this.mClass = tClass;
    }


    /**
     * @param mOnSuccessAndFaultListener 成功回调监听
     * @param context                    上下文
     */
    public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context, Class<?> tClass) {
        this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
        this.context = context;
        progressDialog = new ProgressDialog(context);
        this.mClass = tClass;
    }


    /**
     * @param mOnSuccessAndFaultListener 成功回调监听
     * @param context                    上下文
     * @param showProgress               是否需要显示默认Loading
     */
    public OnSuccessAndFaultSub(OnSuccessAndFaultListener mOnSuccessAndFaultListener, Context context, boolean showProgress) {
        this.mOnSuccessAndFaultListener = mOnSuccessAndFaultListener;
        this.context = context;
        progressDialog = new ProgressDialog(context);
        this.showProgress = showProgress;
    }


    private void showProgressDialog() {
        if (showProgress && null != progressDialog) {
            progressDialog.show();
        }
    }


    private void dismissProgressDialog() {
        if (showProgress && null != progressDialog) {
            progressDialog.dismiss();
        }
    }


    /**
     * 订阅开始时调用
     * 显示ProgressDialog
     */
    @Override
    public void onStart() {
        showProgressDialog();
    }


    /**
     * 完成,隐藏ProgressDialog
     */
    @Override
    public void onComplete() {
        dismissProgressDialog();
        progressDialog = null;
    }


    /**
     * 对错误进行统一处理
     * 隐藏ProgressDialog
     */
    @Override
    public void onError(Throwable e) {
        try {

            if (e instanceof SocketTimeoutException) {//请求超时
            } else if (e instanceof ConnectException) {//网络连接超时
                //                ToastManager.showShortToast("网络连接超时");
                mOnSuccessAndFaultListener.onFault("网络连接超时");
            } else if (e instanceof SSLHandshakeException) {//安全证书异常
                //                ToastManager.showShortToast("安全证书异常");
                mOnSuccessAndFaultListener.onFault("安全证书异常");
            } else if (e instanceof HttpException) {//请求的地址不存在
                int code = ((HttpException) e).code();
                if (code == 504) {
                    //                    ToastManager.showShortToast("网络异常,请检查您的网络状态");
                    mOnSuccessAndFaultListener.onFault("网络异常,请检查您的网络状态");
                } else if (code == 404) {
                    //                    ToastManager.showShortToast("请求的地址不存在");
                    mOnSuccessAndFaultListener.onFault("请求的地址不存在");
                } else {
                    //                    ToastManager.showShortToast("请求失败");
                    mOnSuccessAndFaultListener.onFault("请求失败");
                }
            } else if (e instanceof UnknownHostException) {//域名解析失败
                //                ToastManager.showShortToast("域名解析失败");
                mOnSuccessAndFaultListener.onFault("域名解析失败");
            } else {
                //                ToastManager.showShortToast("error:" + e.getMessage());
                mOnSuccessAndFaultListener.onFault("error:" + e.getMessage());
            }
        } catch (Exception e2) {
            e2.printStackTrace();
        } finally {
            Log.e("OnSuccessAndFaultSub", "error:" + e.getMessage());
            //            mOnSuccessAndFaultListener.onFault("error:" + e.getMessage());
            dismissProgressDialog();
            progressDialog = null;

        }

    }


    /**
     * 当result等于1回调给调用者,否则自动显示错误信息,若错误信息为401跳转登录页面。
     * ResponseBody  body = response.body();//获取响应体
     * InputStream inputStream = body.byteStream();//获取输入流
     * byte[] bytes = body.bytes();//获取字节数组
     * String str = body.string();//获取字符串数据
     */
    @Override
    public void onNext(ResponseBody body) {
        try {
            final String result = CompressUtils.decompress(body.byteStream());
            Log.e("body", result);
            JSONObject jsonObject = new JSONObject(result);
            int resultCode = jsonObject.getInt("ErrorCode");
            if (resultCode == 1) {//成功
                if (mClass == null) {
                    mOnSuccessAndFaultListener.onSuccess(result);
                } else {
                    mOnSuccessAndFaultListener.onSuccess(JSON.parseObject(result, mClass));
                }
            } else {
                String errorMsg = jsonObject.getString("ErrorMessage");
                mOnSuccessAndFaultListener.onFault(errorMsg);
                Log.e("OnSuccessAndFaultSub", "errorMsg: " + errorMsg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 取消ProgressDialog的时候,取消对observable的订阅,同时也取消了http请求
     */
    @Override
    public void onCancelProgress() {
        if (!this.isDisposed()) {
            this.dispose();
        }
    }
}

7、再看看接口实现出调用:

/**
     * 获取数据
     */
    public static void getData(int pageNumber, int userId, DisposableObserver<ResponseBody> subscriber) {
        Map<String, Integer> map = new HashMap<>();
        map.put("start", pageNumber);
        map.put("count", userId);
        Observable<ResponseBody> observable = HttpMethods.getInstance().getHttpApi().getDataForMap(map);
        HttpMethods.getInstance().toSubscribe(observable, subscriber);
    }

8、在Activity与Fragment中使用:

MovieSubscribe.getData(pageNumber, userId, new OnSuccessAndFaultSub(new OnSuccessAndFaultListener() {

            @Override
            public void onSuccess(Object result) {
                //成功
                Toast.makeText(MainActivity.this, "请求成功:", Toast.LENGTH_SHORT).show();
                DoubanMovieBean doubanMovieBean = (DoubanMovieBean) result;

            }

            @Override
            public void onFault(String errorMsg) {
                //失败
                Toast.makeText(MainActivity.this, "请求失败:" + errorMsg, Toast.LENGTH_SHORT).show();
            }
        }, DoubanMovieBean.class));

我将返回数据封装为一个Class<?>实体,当然返回后直接可以赋值给请求之前传入的实体类:DoubanMovieBean。

代码需要的可以联系我,也可以参考@God丶Eye的源码哈!

也可以下载我修改过后的,地址:https://download.csdn.net/download/jocerly/10816728

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值