打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约,由于篇幅字数原因 本章讲解Retrofit配置及各种处理情况
抓住人生中的一分一秒,胜过虚度中的一月一年!
前言
目前较火的网络请求其中有MVP+Retrofit2+okhttp3+Rxjava2,于是我也加入了使用行列,在网上找了许多案例,实际代码开发中解决了一些所谓的坑,总结了些内容与大家共享一下,有不足的地方希望大家提出我将进行再次完善。
实现目标
1、Retrofit创建
2、Retrofit实现Cookie自动化管理
3、Retrofit,Gson解析,请求返回的类型不统一,假如double返回的是null
4、请求参数日志打印
5、统一请求参数添加到请求头中
6、统一请求参数添加到请求body中
7、缓存的拦截器
8、BaseUrl动态切换
9、拦截指定接口,动态更改返回值便于测试
1、Retrofit创建
public class ApiRetrofit { private static final String BASE_SERVER_URL = "www.baidu.com"; private static final int DEFAULT_TIMEOUT = 15; private static ApiRetrofit apiRetrofit; private Retrofit mRetrofit; private ApiServer mApiServer; private String TAG = "ApiRetrofit %s"; public static ApiRetrofit getInstance() { if (apiRetrofit == null) { synchronized (Object.class) { if (apiRetrofit == null) { apiRetrofit = new ApiRetrofit(); } } } return apiRetrofit; } public ApiServer getApiService() { return mApiServer; } public ApiRetrofit() { OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); httpClientBuilder .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .retryOnConnectionFailure(true);//错误重联 mRetrofit = new Retrofit.Builder() .baseUrl(BASE_SERVER_URL) .addConverterFactory(MyGsonConverterFactory.create()) //支持RxJava2 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(httpClientBuilder.build()) .build(); mApiServer = mRetrofit.create(ApiServer.class); } }
2、Retrofit实现Cookie自动化管理
3、Retrofit,Gson解析,请求返回的类型不统一,假如double返回的是null
4、请求参数日志打印
1.第一种办法,依赖第三方库
compile 'com.squareup.okhttp3:logging-interceptor:3.9.1'
配置信息如下
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(); if(BuildConfig.DEBUG){ //显示日志 logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); }else { logInterceptor.setLevel(HttpLoggingInterceptor.Level.NONE); } httpClientBuilder.addInterceptor(logInterceptor);
2.第二种办法,拦截器拦截(个人推荐第二种,可控性高)
给大家推荐一个打印日志库,很漂亮的日志结构
implementation 'com.orhanobut:logger:2.2.0'
/** * 请求访问quest 打印日志 * response拦截器 */ private Interceptor interceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); long startTime = System.currentTimeMillis(); Response response = chain.proceed(chain.request()); long endTime = System.currentTimeMillis(); long duration = endTime - startTime; MediaType mediaType = response.body().contentType(); String content = response.body().string(); Logger.wtf(TAG, "----------Request Start----------------"); printParams(request.body()); Logger.e(TAG, "| " + request.toString() + "===========" + request.headers().toString()); Logger.json(content); Logger.e(content); Logger.wtf(TAG, "----------Request End:" + duration + "毫秒----------"); return response.newBuilder() .body(ResponseBody.create(mediaType, content)) .build(); } }; /** * 请求参数日志打印 * * @param body */ private void printParams(RequestBody body) { if (body != null) { Buffer buffer = new Buffer(); try { body.writeTo(buffer); Charset charset = Charset.forName("UTF-8"); MediaType contentType = body.contentType(); if (contentType != null) { charset = contentType.charset(UTF_8); } String params = buffer.readString(charset); Logger.e(TAG, "请求参数: | " + params); } catch (IOException e) { e.printStackTrace(); } } }
然后在httpClientBuilder中添加拦截
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); httpClientBuilder //打印日志拦截 .addInterceptor(interceptor) .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .retryOnConnectionFailure(true);//错误重联
5、统一请求参数添加到请求头中
/** * 需要头可以添加 请求头 */ public class HeadUrlInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request() .newBuilder() // .addHeader("Content-Type", "text/html; charset=UTF-8") // .addHeader("Vary", "Accept-Encoding") // .addHeader("Server", "Apache") // .addHeader("Pragma", "no-cache") // .addHeader("Cookie", "add cookies here") // .addHeader("Cookie", cookie_name + "=" + cookie_value) .addHeader("XX-Token", App.mToken) .addHeader("XX-Device-Type", "android") // .addHeader("_identity", cookie_value) .build(); return chain.proceed(request); } }
然后在httpClientBuilder中添加拦截
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); httpClientBuilder //添加参数到请求头 .addInterceptor(new HeadUrlInterceptor()) .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .retryOnConnectionFailure(true);//错误重联
6、统一请求参数添加到请求body中
/** * 获取HTTP 添加公共参数的拦截器 * 暂时支持get、head请求&Post put patch的表单数据请求 * * @return */ public class HttpParamsInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); if (request.method().equalsIgnoreCase("GET") || request.method().equalsIgnoreCase("HEAD")) { HttpUrl httpUrl = request.url().newBuilder() .addQueryParameter("version", "1.1.0") .addQueryParameter("devices", "android") .build(); request = request.newBuilder().url(httpUrl).build(); } else { RequestBody originalBody = request.body(); if (originalBody instanceof FormBody) { FormBody.Builder builder = new FormBody.Builder(); FormBody formBody = (FormBody) originalBody; for (int i = 0; i < formBody.size(); i++) { builder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i)); } FormBody newFormBody = builder .addEncoded("version", "1.1.0") .addEncoded("devices", "android") .build(); if (request.method().equalsIgnoreCase("POST")) { request = request.newBuilder().post(newFormBody).build(); } else if (request.method().equalsIgnoreCase("PATCH")) { request = request.newBuilder().patch(newFormBody).build(); } else if (request.method().equalsIgnoreCase("PUT")) { request = request.newBuilder().put(newFormBody).build(); } } else if (originalBody instanceof MultipartBody) { } } return chain.proceed(request); } }
然后在httpClientBuilder中添加拦截
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); httpClientBuilder //添加参数到请求body .addInterceptor(new HttpParamsInterceptor()) .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .retryOnConnectionFailure(true);//错误重联
7、缓存的拦截器
/** * 获得HTTP 缓存的拦截器 * * @return */ public class HttpCacheInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // 无网络时,始终使用本地Cache if (!NetWorkUtils.isConnected()) { request = request.newBuilder() .cacheControl(CacheControl.FORCE_CACHE) .build(); } Response response = chain.proceed(request); if (NetWorkUtils.isConnected()) { //有网的时候读接口上的@Headers里的配置,你可以在这里进行统一的设置 String cacheControl = request.cacheControl().toString(); return response.newBuilder() .header("Cache-Control", cacheControl) .removeHeader("Pragma") .build(); } else { // 无网络时,设置超时为4周 int maxStale = 60 * 60 * 24 * 28; return response.newBuilder() //这里的设置的是我们的没有网络的缓存时间,想设置多少就是多少。 .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) .removeHeader("Pragma") .build(); } } }
然后在httpClientBuilder中添加拦截
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); httpClientBuilder .addInterceptor(new HttpCacheInterceptor()) .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .retryOnConnectionFailure(true);//错误重联
8、BaseUrl动态切换
用了一个博客中民间大神的拦截动态替换baseUrl方法有点问题,我暂时用了一种简单粗暴方法
@FormUrlEncoded @POST("http://www.baidu.com/api/user/edit?") Observable<BaseModel<Object>> getEditInfo(@FieldMap HashMap<String, String> params);
上边的路径是我随便写的,post中写全路径,这个优先级最高,同时设置了baseUrl不受影响
给大家一个专门写动态替换baseUrl连接 传送门
9、拦截指定接口,动态更改返回值便于测试
有时候我们需要返回指定值测试,可能需要空或者null等,迫于无法修改服务器返回数据,也没必要让后台修改数据,所以引发一个问题,如果拦截返回内容并修改指定字段值
public class MockInterceptor implements Interceptor{ @Override public Response intercept(Chain chain) throws IOException { Gson gson = new Gson(); Response response = null; Response.Builder responseBuilder = new Response.Builder() .code(200) .message("") .request(chain.request()) .protocol(Protocol.HTTP_1_0) .addHeader("content-type", "application/json"); Request request = chain.request(); if(request.url().toString().contains("请求的服务器地址/api/index/index?")) { //拦截指定地址 String responseString = "{\n" + "\t\"code\": 1,\n" + "\t\"msg\": \"请求成功\",\n" + "\t\"data\": {\n" + "\t\t\"banner\": [{\n" + "\t\t\t\"id\": 4,\n" + "\t\t}, {\n" + "\t\t\t\"id\": 5,\n" + "\t\t}],\n" + "\t\t\"article\": [{\n" + "\t\t\t\"id\": 6,\n" + "\t\t\t\"user_id\": 3,\n" + "\t\t\t\"title\":null,\n" +//因为我需要个null来判断些情况是否正常,所以手动修改 "\t\t\t\"content\": \"测试帖子内容\",\n" + "\t\t\t\"publish_time\": \"13:02:16\",\n" + "\t\t\t\"view_counts\": 410,\n" + "\t\t\t\"like_counts\": 1,\n" + "\t\t\t\"type\": 1,\n" + "\t\t\t\"cate_type\": 1,\n" + "\t\t\t\"com_counts\": 1,\n" + "\t\t\t\"category_name\": \"生活\",\n" + "\t\t\t\"nickname\": \"测试昵称\",\n" + "\t\t\t\"mobile\": \"150****5395\",\n" + "\t\t}]\n" + "\t}\n" + "}"; responseBuilder.body(ResponseBody.create(MediaType.parse("application/json"), responseString.getBytes()));//将数据设置到body中 response = responseBuilder.build(); //builder模式构建response }else{ response = chain.proceed(request); } return response; } }
然后在httpClientBuilder中添加拦截
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); httpClientBuilder .addInterceptor(new MockInterceptor()) .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .retryOnConnectionFailure(true);//错误重联
文章持续更新中,祝大家开发顺利!