打造終極MVP+Retrofit2+okhttp3+Rxjava2網(wǎng)絡(luò)請(qǐng)求,開發(fā)實(shí)用,簡約,由于篇幅字?jǐn)?shù)原因 本章講解Retrofit配置及各種處理情況
抓住人生中的一分一秒,勝過虛度中的一月一年!
前言
目前較火的網(wǎng)絡(luò)請(qǐng)求其中有MVP+Retrofit2+okhttp3+Rxjava2,于是我也加入了使用行列,在網(wǎng)上找了許多案例,實(shí)際代碼開發(fā)中解決了一些所謂的坑,總結(jié)了些內(nèi)容與大家共享一下,有不足的地方希望大家提出我將進(jìn)行再次完善。
實(shí)現(xiàn)目標(biāo)
1、Retrofit創(chuàng)建
2、Retrofit實(shí)現(xiàn)Cookie自動(dòng)化管理
3、Retrofit,Gson解析,請(qǐng)求返回的類型不統(tǒng)一,假如double返回的是null
4、請(qǐng)求參數(shù)日志打印
5、統(tǒng)一請(qǐng)求參數(shù)添加到請(qǐng)求頭中
6、統(tǒng)一請(qǐng)求參數(shù)添加到請(qǐng)求body中
7、緩存的攔截器
8、BaseUrl動(dòng)態(tài)切換
9、攔截指定接口,動(dòng)態(tài)更改返回值便于測試
1、Retrofit創(chuàng)建
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);//錯(cuò)誤重聯(lián)
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實(shí)現(xiàn)Cookie自動(dòng)化管理
3、Retrofit,Gson解析,請(qǐng)求返回的類型不統(tǒng)一,假如double返回的是null
4、請(qǐng)求參數(shù)日志打印
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.第二種辦法,攔截器攔截(個(gè)人推薦第二種,可控性高)
給大家推薦一個(gè)打印日志庫,很漂亮的日志結(jié)構(gòu)
implementation 'com.orhanobut:logger:2.2.0'
/**
* 請(qǐng)求訪問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();
}
};
/**
* 請(qǐng)求參數(shù)日志打印
*
* @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, "請(qǐng)求參數(shù): | " + 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);//錯(cuò)誤重聯(lián)
5、統(tǒng)一請(qǐng)求參數(shù)添加到請(qǐng)求頭中
/**
* 需要頭可以添加 請(qǐng)求頭
*/
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
//添加參數(shù)到請(qǐng)求頭
.addInterceptor(new HeadUrlInterceptor())
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(true);//錯(cuò)誤重聯(lián)
6、統(tǒng)一請(qǐng)求參數(shù)添加到請(qǐng)求body中
/**
* 獲取HTTP 添加公共參數(shù)的攔截器
* 暫時(shí)支持get、head請(qǐng)求&Post put patch的表單數(shù)據(jù)請(qǐng)求
*
* @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
//添加參數(shù)到請(qǐng)求body
.addInterceptor(new HttpParamsInterceptor())
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(true);//錯(cuò)誤重聯(lián)
7、緩存的攔截器
/**
* 獲得HTTP 緩存的攔截器
*
* @return
*/
public class HttpCacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 無網(wǎng)絡(luò)時(shí),始終使用本地Cache
if (!NetWorkUtils.isConnected()) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (NetWorkUtils.isConnected()) {
//有網(wǎng)的時(shí)候讀接口上的@Headers里的配置,你可以在這里進(jìn)行統(tǒng)一的設(shè)置
String cacheControl = request.cacheControl().toString();
return response.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma")
.build();
} else {
// 無網(wǎng)絡(luò)時(shí),設(shè)置超時(shí)為4周
int maxStale = 60 * 60 * 24 * 28;
return response.newBuilder()
//這里的設(shè)置的是我們的沒有網(wǎng)絡(luò)的緩存時(shí)間,想設(shè)置多少就是多少。
.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);//錯(cuò)誤重聯(lián)
8、BaseUrl動(dòng)態(tài)切換
用了一個(gè)博客中民間大神的攔截動(dòng)態(tài)替換baseUrl方法有點(diǎn)問題,我暫時(shí)用了一種簡單粗暴方法
@FormUrlEncoded
@POST("http://www.baidu.com/api/user/edit?")
Observable<BaseModel<Object>> getEditInfo(@FieldMap HashMap<String, String> params);
上邊的路徑是我隨便寫的,post中寫全路徑,這個(gè)優(yōu)先級(jí)最高,同時(shí)設(shè)置了baseUrl不受影響
給大家一個(gè)專門寫動(dòng)態(tài)替換baseUrl連接 傳送門
9、攔截指定接口,動(dòng)態(tài)更改返回值便于測試
有時(shí)候我們需要返回指定值測試,可能需要空或者null等,迫于無法修改服務(wù)器返回?cái)?shù)據(jù),也沒必要讓后臺(tái)修改數(shù)據(jù),所以引發(fā)一個(gè)問題,如果攔截返回內(nèi)容并修改指定字段值
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("請(qǐng)求的服務(wù)器地址/api/index/index?")) { //攔截指定地址
String responseString = "{\n" +
"\t\"code\": 1,\n" +
"\t\"msg\": \"請(qǐng)求成功\",\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" +//因?yàn)槲倚枰獋€(gè)null來判斷些情況是否正常,所以手動(dòng)修改
"\t\t\t\"content\": \"測試帖子內(nèi)容\",\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()));//將數(shù)據(jù)設(shè)置到body中
response = responseBuilder.build(); //builder模式構(gòu)建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);//錯(cuò)誤重聯(lián)
文章持續(xù)更新中,祝大家開發(fā)順利!