OKhttp網路請求元件的封裝
阿新 • • 發佈:2019-01-23
以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。