Retrofit+RxJava 完美封装

本来在项目中是使用了Volley 然后封装 封装了一个HttpUtils,

但是由于项目中要引入RxJava,所以就顺便替换一下Volley,使用Retrofit+RxJava。


Volley框架,我这里就 不过多介绍了,大家如果想学习的会可以自己去了解下。

http://blog.csdn.net/ysh06201418/article/details/46443235


首先大家可以参考下其他的Retrofit+RxJava的使用方法。

http://blog.csdn.net/byxyrq/article/details/52672202


如果大家对RxJava还不大了解的话,大家可以看下这篇文章。

http://gank.io/post/560e15be2dca930e00da1083


下面我介绍下我是如何封装Retrofit+RxJava的。

这是我公司的后台返回的json数据格式:

{
    data:返回的数据,
    errorCode:200,
    moreInfo:"错误详情"
}

其中'data'中返回的才是我们真正需要的内容,而‘errorCode’不为200,则是后台出错,具体错误信息放在‘moreInfo’中。

而如果直接使用Retrofit+RxJava。如下:

 /**
  * 登录
  */
 @POST
 @FormUrlEncoded
 Observable<UserVO> login(@Url String url , @FieldMap Map<String,Object>param);

如果直接使用该接口,那么需要后台直接返回‘UserVO’的json数据。

但是依照前面所说,后台所有的返回数据都是依照

{
    data:返回的数据,
    errorCode:200,
    moreInfo:"错误详情"
}
这种格式的数据返回。所以请求这个接口后,其实真正返回的数据应该是如下这样:

{
    data:{UserVO对象},
    errorCode:200,
    moreInfo:"错误详情"
}

如上只是一个例子而已,其实所有接口,都是依照这种格式回传,所以在Retrofit+RxJava框架对返回的数据

进行处理之前,需要自己提前处理一下,先判断'errorCode'是否为200,如果为200就是正常请求数据返回,然后

获取到data的数据返回给Retrofit+RxJava框架进行处理。如果不为200,则为错误,根据错误的类型进行相应的处理。

如下:

 /**
     * 统一处理原始数据
     *
     * @param originalResponse
     */
    private static Response dealResponseData(Response originalResponse) {
        String jsonString = null;
        try {
            BufferedSource bufferedSource = originalResponse.body().source();
            jsonString = bufferedSource.readString(Charset.forName("utf-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!jsonString.trim().startsWith("{") && !jsonString.trim().startsWith("[")) {
            return onSuccess(originalResponse, jsonString);
        }
        ResponseMessageBean msgBean = ResponseMessageBean.analyseReponse(jsonString);
        if (msgBean == null) return onSuccess(originalResponse, msgBean.data.toString());
        if (msgBean != null && (msgBean.errorCode == 200)) {
            showError = true;
            if (msgBean.data != null) {
                return onSuccess(originalResponse, msgBean.data.toString());
            } else {
                return originalResponse.newBuilder().body(null).build();
            }
        } else {
            onFailed(msgBean);
            throw new RuntimeException(msgBean.moreInfo.toString());
        }
    }
请求之后返回一个Response对象,预先处理完成之后返回另一新的Response对象,这样就可以实现刚才的目的了。

ResponseMessageBean其实就是最外面的对象。

onSuccess方法中,其实主要对处理后的数据重新组装成一个新的Response对象

private static Response onSuccess(Response originalResponse, String content) {
        return originalResponse.newBuilder().
                body(ResponseBody.create(null, content)).
                build();
    }
方法虽然简单,但是这里要注意一点,不能新建一个新的Response返回,因为你Response里面其实是有多数据的,你就使用原来的Response,

然后把body里面的内容替换下就好了。

其实这里还有一个onFailed方法,主要是后台请求报错了,没有返回200,就根据你自己后台定义的值来判断具体错误,然后具体处理,这里

我就不再具体描述了。


前面描述的是data返回值是一个对象,其实是一个数组也是一样的,只要你在泛型里面写的是List那么就会返回一个List对象,如下:

 @POST
    @FormUrlEncoded
    Observable<List<UserVO>> userList(@Url String url , @FieldMap Map<String,Object>param);

这样处理的数据其实是这种格式的

{
    data:[{},{}],
    errorCode:200,
    moreInfo:"错误详情"
}

对了,我这里一直没有讲为什么Retrofit+RxJava会对返回的对象进行自动转换,因为我前面已经放了几篇文章,希望大家能先阅读了

一些基础的知识后,再来看这篇文章的具体封装,可能对你的帮助会更大一点。

Retrofit在初始化:

 Retrofit retrofit = new Retrofit.Builder().baseUrl(UrlConstant.BASEURL).
                addConverterFactory(GsonConverterFactory.create()).
                addCallAdapterFactory(RxJavaCallAdapterFactory.create()).
                client(mOkHttpClient).
                build();

addConverterFactory其实是为了对返回内容的提供一个转换器。

这里官方实现了很多种转换器,如下

Gson: com.squareup.retrofit:converter-gson

Jackson: com.squareup.retrofit:converter-jackson

Moshi: com.squareup.retrofit:converter-moshi

Protobuf: com.squareup.retrofit:converter-protobuf

Wire: com.squareup.retrofit:converter-wire

Simple XML: com.squareup.retrofit:converter-simplexml

一共有这么几种,大家可以根据自己的返回格式和喜好,选择对应的转换器,我这里选择了Gson转换器。

记得选择转换器,要引入对应的依赖,这里我就不再过多赘述了。


我这里为什么要讲这个转换器呢?因为在项目的的过程中,我发现了有这样的一个需求,就是返回值中的data不是对象,也不是数组,

如下:

{
    data:"你好",
    errorCode:200,
    moreInfo:"错误详情"
}
或者是其他‘boolean’,‘int’等基础类型。就会出错。所以我这里重新对gson转换器做了封装,如下

public class TWGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {

    private final Gson gson;
    private final TypeAdapter<T> adapter;
    private final TypeToken<?> typeToken;

    TWGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter, TypeToken<?> typeToken) {
        this.gson = gson;
        this.adapter = adapter;
        this.typeToken = typeToken;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        Object obj = checkBasicType(value);
        if (obj != null) return (T) obj;
        JsonReader jsonReader = gson.newJsonReader(value.charStream());
        try {
            return adapter.read(jsonReader);
        } finally {
            value.close();
        }
    }

    /**
     * 如果是基本类型就转化后,返回
     */
    private Object checkBasicType(ResponseBody value) throws IOException {
        String typeName = typeToken.getRawType().getSimpleName();
        if ("String".equals(typeName)) {
            return value.string();
        } else if ("Boolean".equals(typeName)) {
            return Boolean.parseBoolean(value.string());
        } else if ("Integer".equals(typeName)) {
            return Integer.parseInt(value.string());
        }
        return null;
    }
}
在方法中判断是否为基础类型,如果为基础类型,或者String 就通过处理后,直接返回,如果不是,则还是通过原来的转换器转换。

这样子就可以适用所有情况了。

在我的项目中,有一个场景,就是需要在请求时,自定义请求头,虽然Retrofit有@Head,但是由于每次请求都需要设置,还是直接封装

在里面比较好,代码如下:

private static Request.Builder createRequestHeader(Request.Builder builder) {
        builder.header("Content-Type",
                "application/x-www-form-urlencoded");
        builder.header("User-Agent", getUserAgent());
        return builder;
    }

其实就是创建一个新的Request返回而已。

然后项目中又说要使用https来访问,所以我又加了下面的代码来支持:

if (mOkHttpClient == null) {
                    try {
                        X509TrustManager xtm = new X509TrustManager() {
                            @Override
                            public void checkClientTrusted(X509Certificate[] chain, String authType) {
                            }

                            @Override
                            public void checkServerTrusted(X509Certificate[] chain, String authType) {
                            }

                            @Override
                            public X509Certificate[] getAcceptedIssuers() {
                                X509Certificate[] x509Certificates = new X509Certificate[0];
                                return x509Certificates;
                            }
                        };

                        SSLContext sslContext = null;
                        try {
                            sslContext = SSLContext.getInstance("SSL");

                            sslContext.init(null, new TrustManager[]{xtm}, new SecureRandom());

                        } catch (NoSuchAlgorithmException e) {
                            e.printStackTrace();
                        } catch (KeyManagementException e) {
                            e.printStackTrace();
                        }
                        HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
                            @Override
                            public boolean verify(String hostname, SSLSession session) {
                                return true;
                            }
                        };

                        // 指定缓存路径,缓存大小100Mb
                        Cache cache = new Cache(new File(mContext.getCacheDir(), "HttpCache"), 1024 * 1024 * 100);
                        mOkHttpClient = new OkHttpClient.Builder().
                                addInterceptor(mRewriteCacheControlInterceptor).
                                retryOnConnectionFailure(false).
                                connectTimeout(30, TimeUnit.SECONDS).
                                sslSocketFactory(sslContext.getSocketFactory()).
                                hostnameVerifier(DO_NOT_VERIFY).
                                cache(cache).
                                build();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

并且还指定了缓存还有超时时间,重试次数,等等。

以上情况其实已经适用我当前项目的所以场景了。


下面是附上所有代码:

public class RetrofitHelper {
    private static ServerApi api;
    private static OkHttpClient mOkHttpClient;
    private static Context mContext;
    private static boolean showError = true;

    /**
     * 启动后初始化
     */
    public static void init(Context context) {
        mContext = context;
        initOkHttpClient();
        Retrofit retrofit = new Retrofit.Builder().baseUrl(UrlConstant.BASEURL).
                addConverterFactory(GsonConverterFactory.create()).
                addCallAdapterFactory(RxJavaCallAdapterFactory.create()).
                client(mOkHttpClient).
                build();
        api = retrofit.create(ServerApi.class);
    }

    /**
     * 重置baseUrl
     */
    public static void resetBaseUrl() {
        Retrofit retrofit = new Retrofit.Builder().baseUrl(UrlConstant.BASEURL).
                addConverterFactory(TWGsonConverterFactory.create()).
                addCallAdapterFactory(RxJavaCallAdapterFactory.create()).
                client(mOkHttpClient).
                build();
        api = retrofit.create(ServerApi.class);
    }


    public static ServerApi getApi() {
        return api;
    }

    /**
     * 统一处理原始数据
     *
     * @param originalResponse
     */
    private static Response dealResponseData(Response originalResponse) {
        String jsonString = null;
        try {
            BufferedSource bufferedSource = originalResponse.body().source();
            jsonString = bufferedSource.readString(Charset.forName("utf-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!jsonString.trim().startsWith("{") && !jsonString.trim().startsWith("[")) {
            return onSuccess(originalResponse, jsonString);
        }
        ResponseMessageBean msgBean = ResponseMessageBean.analyseReponse(jsonString);
        if (msgBean == null) return onSuccess(originalResponse, msgBean.data.toString());
        if (msgBean != null && (msgBean.errorCode == 200)) {
            showError = true;
            if (msgBean.data != null) {
                return onSuccess(originalResponse, msgBean.data.toString());
            } else {
                return originalResponse.newBuilder().body(null).build();
            }
        } else {
            onFailed(msgBean);
            throw new RuntimeException(msgBean.moreInfo.toString());
        }
    }

    /**
     * 初始化okHttp
     */
    private static void initOkHttpClient() {
        if (mOkHttpClient == null) {
            synchronized (RetrofitHelper.class) {
                Interceptor mRewriteCacheControlInterceptor = new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        if (!NetworkUtils.isNetworkConnected(mContext)) {
                            request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
                        }
                        /**
                         * 统一设置请求头
                         */
                        Request newRequest = createRequestHeader(request.newBuilder()).build();
                        Response originalResponse = chain.proceed(newRequest);
                        //如果是重定向,那么就执行重定向后返回数据。
                        if (originalResponse.isRedirect()) {
                            Request redirectRequest = request.newBuilder().url(originalResponse.header("location")).build();
                            originalResponse = chain.proceed(redirectRequest);
                        }
                        originalResponse = dealResponseData(originalResponse);
                        return originalResponse;
                    }
                };

                if (mOkHttpClient == null) {
                    try {
                        X509TrustManager xtm = new X509TrustManager() {
                            @Override
                            public void checkClientTrusted(X509Certificate[] chain, String authType) {
                            }

                            @Override
                            public void checkServerTrusted(X509Certificate[] chain, String authType) {
                            }

                            @Override
                            public X509Certificate[] getAcceptedIssuers() {
                                X509Certificate[] x509Certificates = new X509Certificate[0];
                                return x509Certificates;
                            }
                        };

                        SSLContext sslContext = null;
                        try {
                            sslContext = SSLContext.getInstance("SSL");

                            sslContext.init(null, new TrustManager[]{xtm}, new SecureRandom());

                        } catch (NoSuchAlgorithmException e) {
                            e.printStackTrace();
                        } catch (KeyManagementException e) {
                            e.printStackTrace();
                        }
                        HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
                            @Override
                            public boolean verify(String hostname, SSLSession session) {
                                return true;
                            }
                        };

                        // 指定缓存路径,缓存大小100Mb
                        Cache cache = new Cache(new File(mContext.getCacheDir(), "HttpCache"), 1024 * 1024 * 100);
                        mOkHttpClient = new OkHttpClient.Builder().
                                addInterceptor(mRewriteCacheControlInterceptor).
                                retryOnConnectionFailure(false).
                                connectTimeout(30, TimeUnit.SECONDS).
                                sslSocketFactory(sslContext.getSocketFactory()).
                                hostnameVerifier(DO_NOT_VERIFY).
                                cache(cache).
                                build();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }


    private static Response onSuccess(Response originalResponse, String content) {
        return originalResponse.newBuilder().
                body(ResponseBody.create(null, content)).
                build();
    }


    /**
     * errorCode 不为200
     *
     * @param msgBean
     */
    private static void onFailed(ResponseMessageBean msgBean) {
        String alert = "";
        if (msgBean == null) {
            return;
        }
        if (msgBean.errorCode != 200) {
            if (msgBean.errorCode == 401) {
                Observable.create(new Observable.OnSubscribe<Object>() {
                    @Override
                    public void call(Subscriber<? super Object> subscriber) {
                        DialogUtils.alertUserUnauthorized(mContext);
                    }
                }).subscribeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Object>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                    }

                    @Override
                    public void onNext(Object o) {
                    }
                });

                return;
            } else if (msgBean.errorCode == 301) {
                alert = "服务器繁忙,请稍后再试";
            } else if (msgBean.errorCode == 302) {
                alert = "此次访问已失效哦";
            } else if (msgBean.errorCode == 403) {
                alert = "接口临时关闭,请稍后再试";
            } else if (msgBean.errorCode == 500) {
                alert = "服务器繁忙,请稍后再试";
            } else if (msgBean.errorCode == -1) {
                showError = true;
                if (msgBean.moreInfo != null) {
                    alert = msgBean.moreInfo.toString();
                }
            } else {
                return;
            }
            final String alertStr = alert;

            Observable.create(new Observable.OnSubscribe<Object>() {
                @Override
                public void call(Subscriber<? super Object> subscriber) {
                    //这里有 window bad token 错误
                    try {
                        if (showError && !TextUtils.isEmpty(alertStr)) {
                            ToastUtils.showToast(mContext, alertStr);
                            showError = false;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).subscribeOn(AndroidSchedulers.mainThread()).subscribe();
        }
    }

    /**
     * 统一处理请求头部数据
     *
     * @return
     */

    private static Request.Builder createRequestHeader(Request.Builder builder) {
        builder.header("Content-Type",
                "application/x-www-form-urlencoded");
        builder.header("User-Agent", getUserAgent());
        return builder;
    }


    public static String getUserAgent() {
        UserAgentBean userAgent = new UserAgentBean();
        userAgent.setDeviceToken(AppUtils.getDeviceId(mContext));
        userAgent.setClient("Android");
        userAgent.setChannel("xunbao");
        if (User.getCurrent() != null) {
            userAgent.setUserId(User.getCurrent().getId());
        }
        PackageInfo info = AppUtils.getPackageInfo(mContext);
        if (info != null) {
            userAgent.setBuild(info.versionCode + "");
            userAgent.setVersion(info.versionName);
        }
        userAgent.setScreenSize(
                DisplayUtils.getScreenSize(mContext));
        userAgent.setSafeToken(SharePerferenceUtils.getString(mContext,PreferenceConstant.SAFE_TOKEN));
        return userAgent.toString();
    }

}






  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值