1. 程式人生 > >OKhttp網路請求元件的封裝

OKhttp網路請求元件的封裝

    以OKhttp為例,對網路請求元件進行封裝。  如果我們不封裝元件的話也可以使用,但是每次呼叫都需要重寫方法,就會產生大量程式碼,並且全部暴露在activity中,不利於後  續的管理。所以封裝是必不可少的。

接下來分析就分析下如何封裝OKhttp,  傳送一個網路請求需要三個功能模組,分別是 Request處理,OKhttp核心處理,callback處理  如下圖所示:


首先進行第一部分Request處理部分的封裝,直接上程式碼吧,程式碼中都有註釋的

1:RequestParams檔案用於封裝所有的請求到HashMap中

package com.yongninggo.ok_http.Request;

import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 小馬哥
 * @function 封裝所有的請求引數到HashMap中
 */

public class RequestParams {

    public ConcurrentHashMap<String,String> urlParams = new ConcurrentHashMap<String,String>();
    public ConcurrentHashMap<String,Object> fileParams = new ConcurrentHashMap<String,Object>();

    public RequestParams (){
        this((Map<String,String>)null);
    }

    public RequestParams(Map<String, String> source) {
        if (source != null) {
            for (Map.Entry<String, String> entry : source.entrySet()) {
                put(entry.getKey(), entry.getValue());
            }
        }
    }

    public RequestParams(final String key, final String value) {
        this(new HashMap<String, String>() {
            {
                put(key, value);
            }
        });
    }

    //將key對應的value放置urlParams <HashMap> 中
    public void put(String key, String value) {
        if (key != null && value != null) {
            urlParams.put(key, value);
        }
    }

    public void put(String key, Object object) throws FileNotFoundException {

        if (key != null) {
            fileParams.put(key, object);
        }
    }

    public boolean hasParams() {
        if(urlParams.size() > 0 || fileParams.size() > 0){

            return true;
        }
        return false;
    }

}

2: CommonRequest檔案類,用於接收請求引數,為我們生成Request物件  (2個方法 (Post and Get))
package com.yongninggo.ok_http.Request;

import java.util.Map;

import okhttp3.FormBody;
import okhttp3.Request;

/**
 * @author 小馬哥
 * @function 接收請求引數,為我們生成request物件
 */

public class CommonRequest {

    /**
     *
     * @param url
     * @param params
     * @return 返回一個建立好的request物件
     */
    public static Request CreatePostRequest (String url,RequestParams params){
        //構建者物件
        FormBody.Builder FormBodyBuilder = new FormBody.Builder();

        if (params != null) {
            for (Map.Entry<String,String>entry :params.urlParams.entrySet()){
                //將請求引數新增到請求構建類中
                FormBodyBuilder.add(entry.getKey(),entry.getValue());
            }
        }
        //通過build方法獲取到真正的請求體物件
        FormBody formBody = FormBodyBuilder.build();
        return new Request.Builder().url(url).post(formBody).build();
    }

    /**
     *
     * @param url
     * @param params
     * @return 返回一個Get型別的請求
     */
    public static Request CreateGetRequest (String url,RequestParams params){
        //因為涉及到字串的拼接,所以使用stringbuilder的效率更高  Get請求‘問號後面帶引數’
        StringBuilder urlBuilder = new StringBuilder(url).append("?");
        if (params != null) {
            for (Map.Entry<String,String>entry :params.urlParams.entrySet()){
                urlBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        return new Request.Builder().url(urlBuilder.substring(0,urlBuilder.length()-1)).get().build();
    }

}

到目前為止,Request處理部分已經完成了封裝 接下來進行核心OKhttp進行封裝

commonokhttpClient類是對okhttp進行封裝,完成傳送請求,引數的配置,https的支援
package com.yongninggo.ok_http;

import com.yongninggo.ok_http.https.HttpsUtils;

import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;

/**
 * @function 請求傳送,請求引數的配置,https的支援
 * @author 小馬哥
 */

public class CommonOkhttpClient {

    private static final int TIME_OUT = 30; //超時引數為30秒
    private static OkHttpClient okHttpClient;

    //為我們的Client配置引數
    static {
        //建立構建者物件
        OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
        okHttpBuilder.connectTimeout(TIME_OUT, TimeUnit.SECONDS);//填充超時時間
        okHttpBuilder.readTimeout(TIME_OUT,TimeUnit.SECONDS);//填充讀超時間
        okHttpBuilder.writeTimeout(TIME_OUT,TimeUnit.SECONDS);//填充寫超時間

        okHttpBuilder.followRedirects(true);  //允許服務重定向

        //新增https支援
        okHttpBuilder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
        //設定SSLFactory
        okHttpBuilder.sslSocketFactory(HttpsUtils.getSslSocketFactory());
        //生成Client物件
        okHttpClient = okHttpBuilder.build();
    }

    //傳送具體的請求   返回Call
    public static Call sendRequest (Request request, okhttp3.Callback callback){

        Call call = okHttpClient.newCall(request);
        call.enqueue(callback);
        return call;
    }

}

HttpsUtils檔案是設定加密的SSLfactory的信任管理類

package com.yongninggo.ok_http.https;

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;

/**
 * Created by 14487 on 2017/6/30.
 */

public class HttpsUtils {


    //生成一個加密型別的SSLSocketFactory
    public static SSLSocketFactory getSslSocketFactory (){
        //1:生成一個信任管理器類
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };

        //2:建立加密上下文
        SSLContext sslContext = null;
        try {
            //和伺服器要保持一致的演算法型別
            sslContext = SSLContext.getInstance("SSL");
            X509TrustManager[] trustArray = new X509TrustManager[]{
                    trustManager
            };
            sslContext.init(null,trustArray,new SecureRandom());
        }
        catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return sslContext.getSocketFactory();
    }
}

前兩步的封裝現在已經完成了,就剩Callback的封裝了。

callback需要處理的功能有 1:處理異常; 2:轉發訊息到UI執行緒 ;  3:將json轉化成對應的實體物件  3:執行回撥

先新建2個檔案  DisposeDataListener 處理我們的自定義監聽事件:成功,失敗,下載,上傳操作等
package com.yongninggo.ok_http.listener;

/**
 * 自定義事件監聽
 */

public interface DisposeDataListener {
    //請求失敗呼叫
    public void onFailure (Object responseObj);


    //請求成功呼叫
    public void onSuccess (Object reasonObj);
}
DisposeDataHandle 處理json物件到實體物件的轉換
package com.yongninggo.ok_http.listener;

/**
 * @author chao
 * @function json物件裝換成實體物件
 */

public class DisposeDataHandle {

    public DisposeDataListener listener = null;
    public Class<?> aClass = null;

    //資料原封不動
    public DisposeDataHandle(DisposeDataListener listener){
        this.listener = listener;
    }
    //資料轉化為json實體物件
    public DisposeDataHandle (DisposeDataListener listener,Class<?> clazz){
        this.listener = listener;
        this.aClass = clazz;
    }
}

上面是作為回撥內容的處理功能,主要啊功能在CommonJSonCallback中實現: 首先建一個Exception用來收集所有的錯誤資訊
package com.yongninggo.ok_http.Exception;

/**
 * @author chao
 * @function 自定義異常處理
 */

public class OkhttpException extends Exception {
    private static final long serialVersionUID = 1L;
    private int ecode;
    private Object emsg;
    public OkhttpException(int ecode,Object emsg){
        this.ecode = ecode;
        this.emsg = emsg;
    }
    public int getEcode (){
        return ecode;
    }
    public Object getEmsg (){
        return emsg;
    }
}

  好了,完成jsoncallback程式碼附上,此時封裝基本就已經實現了,接下來測試呼叫看是否成功。
package com.yongninggo.ok_http.Response;

import android.os.Handler;
import android.os.Looper;

import com.yongninggo.ok_http.Exception.OkhttpException;
import com.yongninggo.ok_http.listener.DisposeDataHandle;
import com.yongninggo.ok_http.listener.DisposeDataListener;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Response;


/**
 * @function 處理json的回撥響應
 */

public class CommonjsonCallback  {

    //與伺服器返回欄位的對應關係
    protected final String RESULT_CODE = "exode";
    protected final int RESULT_CODE_VALUE = 0;
    protected final String ERROR_MSG = "emsg";
    protected final String EMPTY_MSG = "";

    //自定義異常型別
    protected final int NETWORK_ERROR = -1;//網路錯誤
    protected final int JSON_ERROR = -2;//json解析錯誤
    protected final int OTHER_ERROR = -3;//其他錯誤

    private Handler mDeliveryHandler;//進行訊息的轉發,將子執行緒的資料轉發到UI執行緒
    private DisposeDataListener Listener;
    private Class<?> aClass;

    public CommonjsonCallback (DisposeDataHandle handle){
        this.Listener = handle.listener;
        this.aClass = handle.aClass;
        this.mDeliveryHandler = new Handler(Looper.getMainLooper());
    }

    //請求失敗處理
    public void onFailure (final Call call, final IOException e){
            mDeliveryHandler.post(new Runnable() {
                @Override
                public void run() {
                    //失敗的話將資訊傳入Exception中
                    Listener.onFailure(new OkhttpException(NETWORK_ERROR,e));
                }
            });
    }

    //請求成功響應處理
    public void onResponse (final Call call, final Response response) throws IOException{
        final String result = response.body().string();
        mDeliveryHandler.post(new Runnable() {
            @Override
            public void run() {
                handleResponse (result);
            }

        });
    }
    //處理伺服器返回的響應資料
    private void handleResponse(Object responseObj) {
        //為了保證程式碼的健壯性
        if (responseObj == null && responseObj.toString().trim().equals(" ")){
            Listener.onFailure(new OkhttpException(NETWORK_ERROR,EMPTY_MSG));
            return;
        }

        try {
            //開始嘗試解析json
            JSONObject reuslt = new JSONObject(responseObj.toString());
            //從json物件中取出我們的響應碼,若為0(與伺服器一致),則是正常的響應
            if (reuslt.has(RESULT_CODE)){
                if (reuslt.getInt(RESULT_CODE) == RESULT_CODE_VALUE){
                    if (aClass == null) {
                        Listener.onSuccess(responseObj);
                    } else {
                        //將json物件轉化為實體物件
                        Gson gson = new Gson();
                        Object obj = gson.fromJson(responseObj.toString(),aClass);

                        if (obj != null) {
                            Listener.onSuccess(obj);
                        } else {
                            //返回的不是合法的json
                            Listener.onFailure(new OkhttpException(JSON_ERROR,EMPTY_MSG));
                        }
                    }
                }
                else {
                    //將伺服器返回給我們的異常回調到應用層去處理
                    Listener.onFailure(new OkhttpException(OTHER_ERROR,reuslt.get(RESULT_CODE)));
                }
            }
        }
        catch (JSONException e) {
            Listener.onFailure(new OkhttpException(OTHER_ERROR,e.getMessage()));
        }

    }
}


將json轉化為實體物件是利用Gson轉json的,  其實就是新增一個json的依賴,在下載一個GsonFormat就可以了。 (這一步可以百度就知道了) 測試呼叫格式
private void test (){
        CommonOkhttpClient.sendRequest(CommonRequest.CreateGetRequest("http://www.baidu.com/",null),new CommonJsonCallback(new DisposeDataHandle(new DisposeDataListener() {
            @Override
            public void onFailure(Object responseObj) {

            }

            @Override
            public void onSuccess(Object reasonObj) {
                
                
            }
        })));
    }

測試成功,  over。