Retrofit+RxJava 完美封装

来源:互联网 发布:淘宝淘友在哪里看 编辑:程序博客网 时间:2024/05/17 04:10

本来在项目中是使用了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 0