1. 程式人生 > >OkHttp中Interceptor攔截器之公共引數請求封裝

OkHttp中Interceptor攔截器之公共引數請求封裝

前言

之前在面試的時候遇到這樣的一個問題,那就是如果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
    ":value }

    其中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一下看下就明白了。
我們可以看到下圖中上面的引數是隻有一個,但是第二行就已經改變了,新增過了我們需要的公共引數,並且還有我們請求的引數。符合我們的要求。

參考文章