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 login(@Url String url , @FieldMap Mapparam);


如果直接使用该接口,那么需要后台直接返回‘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> userList(@Url String url , @FieldMap Mapparam);

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

{ 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 implements Converter {private final Gson gson; private final TypeAdapter adapter; private final TypeToken typeToken; TWGsonResponseBodyConverter(Gson gson, TypeAdapter 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() { @Override public void call(Subscriber subscriber) { DialogUtils.alertUserUnauthorized(mContext); } }).subscribeOn(AndroidSchedulers.mainThread()).subscribe(new Observer() { @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() { @Override public void call(Subscriber 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(); }}








【Retrofit+RxJava 完美封装】

    推荐阅读