OkHttp中Interceptor攔截器之公共引數請求封裝
阿新 • • 發佈:2019-01-24
前言
之前在面試的時候遇到這樣的一個問題,那就是如果app中所有的請求都要加入一些引數(例如 版本號、手機號、登入使用者名稱、token等。。。)那麼怎麼做才能實現,而不至於在每次請求的時候都去進行新增這些請求頭。其實這個問題,剛開始我是拒絕的(之前沒有遇到過這樣的需求)。當時只是想著可以使用okhttp體用的攔截器Interceptor來進行實現,但是具體的實現還是來到了今天。
Interceptor說明
在okhttp-wiki裡面專門有一篇介紹Interceptor的(點選我跳轉到Interceptor連結)裡面有這樣的一句話
Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.
攔截器是一種強大的機制,可以監視、重寫和重試呼叫
Interceptor幾大作用
- Application Interceptors 應用攔截器
- Network Interceptors 網路攔截器
- Choosing between application and network interceptors 在應用和網路攔截器之間做選擇
- Rewriting Requests 重寫請求
- Rewriting Responses 重寫響應
進入主題
先來看下我們的需求
需求約束
2、客戶端請求API的資料格式為json形式:
{ "publicParams":{}, "key1":value, "key2
其中publicParams為公共引數,每個API介面必須傳遞
3、公共引數
欄位 型別 說明 imei string 移動裝置身份(非必須) model 手機型號、裝置名稱 la string 系統語言 resolution string 解析度(格式:1920*1080) densityScaleFactor string 密度比例 sdk int SDK版本號 os string 系統原始碼控制值
解決方法
我們就是利用Interceptor攔截器,對每次的網路請求進行攔截,然後拿到請求url,並對url進行改造來加入我們需要的公共引數,進行和請求引數的拼接,然後構造request,通過chain.proceed(request)進行改造。
第一步
- 構建一個CommonParamsInterceptor.class類繼承自Interceptor
- 複寫intercept(Chain chain)方法,我們需要用到chain.request()來獲取到request物件
- request裡面的方法
這裡我們只需要用到url()方法(主要獲取到api地址和請求引數,並進行改造),用到method()方法來判斷get請求和post請求。
這個時候我們看下程式碼怎麼進行編寫吧。
@Override
public Response intercept(Chain chain) throws IOException {
//獲取到request
Request request = chain.request();
//獲取到方法
String method = request.method();
//公共引數hasmap
try {
//新增公共引數
HashMap<String, Object> commomParamsMap = new HashMap<>();
commomParamsMap.put(Constants.IMEI, DeviceUtils.getIMEI(mContext));
commomParamsMap.put(Constants.MODEL, DeviceUtils.getModel());
commomParamsMap.put(Constants.LANGUAGE, DeviceUtils.getLanguage());
commomParamsMap.put(Constants.os, DeviceUtils.getBuildVersionIncremental());
commomParamsMap.put(Constants.RESOLUTION, DensityUtil.getScreenW(mContext) + "*" + DensityUtil.getScreenH(mContext));
commomParamsMap.put(Constants.SDK, DeviceUtils.getBuildVersionSDK() + "");
commomParamsMap.put(Constants.DENSITY_SCALE_FACTOR, mContext.getResources().getDisplayMetrics().density + "");
//get請求的封裝
if (method.equals("GET")) {
//獲取到請求地址api
HttpUrl httpUrlurl = request.url();
HashMap<String, Object> rootMap = new HashMap<>();
//通過請求地址(最初始的請求地址)獲取到引數列表
Set<String> parameterNames = httpUrlurl.queryParameterNames();
for (String key : parameterNames) { //迴圈引數列表
if (Constants.PARM.equals(key)) { //判斷是否有匹配的欄位 這個類似於 /xxx/xxx?p={} 匹配這個p
String oldParamsJson = httpUrlurl.queryParameter(Constants.PARM);
if (oldParamsJson != null) { //因為有的是沒有這個p={"page":0} 而是直接/xxx/index的
HashMap<String, Object> p = mGson.fromJson(oldParamsJson, HashMap.class); //原始引數
if (p != null) {
for (Map.Entry<String, Object> entry : p.entrySet()) {
rootMap.put(entry.getKey(), entry.getValue());
}
}
}
} else {
rootMap.put(key, httpUrlurl.queryParameter(key));
}
}
//String oldParamJson = httpUrlurl.queryParameter(Constants.PARM);
// if (oldParamJson != null) {
//
// }
//把原來請求的和公共的引數進行組裝
rootMap.put("publicParams", commomParamsMap); //重新組裝
String newJsonParams = mGson.toJson(rootMap); //裝換成json字串
String url = httpUrlurl.toString();
int index = url.indexOf("?");
if (index > 0) {
url = url.substring(0, index);
}
url = url + "?" + Constants.PARM + "=" + newJsonParams; //拼接新的url
request = request.newBuilder().url(url).build(); //重新構建請求
//post請求的封裝
} else if (method.equals("POST")) {
// FormBody.Builder builder = new FormBody.Builder();
// builder.addEncoded("phone","phone");
RequestBody requestBody = request.body();
HashMap<String, Object> rootMap = new HashMap<>();
if (requestBody instanceof FormBody) {
for (int i = 0; i < ((FormBody) requestBody).size(); i++) {
rootMap.put(((FormBody) requestBody).encodedName(i), ((FormBody) requestBody).encodedValue(i));
}
} else {
//buffer流
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
String oldParamsJson = buffer.readUtf8();
rootMap = mGson.fromJson(oldParamsJson, HashMap.class); //原始引數
rootMap.put("publicParams", commomParamsMap); //重新組裝
String newJsonParams = mGson.toJson(rootMap); //裝換成json字串
request = request.newBuilder().post(RequestBody.create(JSON, newJsonParams)).build();
}
}
} catch (JsonSyntaxException e) {
e.printStackTrace();
}
//最後通過chain.proceed(request)進行返回
return chain.proceed(request);
}
第二步
當我們把自定義的Interceptor構建完成之後,我們需要在Okhttp中進行使用。
return new OkHttpClient.Builder()
//HeadInterceptor 實現了Intercepter 用來網Request Header新增一些相關資料 如APP版本 token資訊
// .addInterceptor(new HttpLoggingInterceptor())
//新增自定義的攔截器,完成公共引數的封裝
.addInterceptor(new CommonParamsInterceptor(gson,application))
.connectTimeout(10, TimeUnit.SECONDS)//連結超時
.readTimeout(10, TimeUnit.SECONDS)//設定讀取超時
.build();
這個時候就可以了,我們來看下攔截之前和加入攔截器之後的請求url,看看是否起到了作用。
這個時候我們debug一下看下就明白了。
我們可以看到下圖中上面的引數是隻有一個,但是第二行就已經改變了,新增過了我們需要的公共引數,並且還有我們請求的引數。符合我們的要求。