標題: 商城開發筆記-06-OkHttp的使用以及簡單封裝 [打印本頁]
作者: 51黑bing 時間: 2016-3-22 17:20
標題: 商城開發筆記-06-OkHttp的使用以及簡單封裝
一、Android中網絡請求的進化
Android開發中網絡編程是必不可少的,不接入互聯網的APP就沒有盈利可言。廢話不多說了,下面請看Android中網絡請求的進化圖:
HttpURLConnection,Apache HTTP Client,Volley到現在的OKHttp,可謂天外有天,人外有人。為什么OKHttp會這么火呢,相信下面的介紹會告訴你答案。
二、OKHttp的簡介
首先,給出OKHttp的項目地址:https://github.com/square/okhttp
Android為我們提供了兩種HTTP交互的方式: HttpURLConnection 和 ApacheHTTP Client,雖然兩者都支持HTTPS,流的上傳和下載,配置超時,IPv6和連接池,已足夠滿足我們各種HTTP請求的需求。但更高效的使用HTTP可以讓您的應用運行更快、更節省流量。而OkHttp庫就是為此而生。
OkHttp是一個高效的HTTP庫:
1.支持 SPDY ,共享同一個Socket來處理同一個服務器的所有請求
2.如果SPDY不可用,則通過連接池來減少請求延時
3.無縫的支持GZIP來減少數據流量
4. 緩存響應數據來減少重復的網絡請求
· 一般的get請求
· 一般的post請求
· 基于Http的文件上傳
· 文件下載
· 加載圖片
· 支持請求回調,直接返回對象、對象集合
· 支持session的保持
OkHttp會從很多常用的連接問題中自動恢復。如果您的服務器配置了多個IP地址,當第一個IP連接失敗的時候,會自動嘗試下一個IP。OkHttp還處理了代理服務器問題和SSL握手失敗問題。(并發)
使用 OkHttp 無需重寫您程序中的網絡代碼。OkHttp實現了幾乎和java.net.HttpURLConnection一樣的API。如果您用了 Apache HttpClient,則OkHttp也提供了一個對應的okhttp-apache 模塊。
從上面的簡單介紹中可以知道,雖然在編程上面并不會簡潔很多,但是OKHttp內部的一些功能能夠幫助我們自動完成一些很復雜的操作,筆者個人認為最大的賣點就是大大節省用戶的流量。
三、OKHttp的基本使用
1、在Gradle中引入CardView的依賴庫。
compile 'com.squareup.okhttp:okhttp:2.7.2'
2、OKHttp在使用之前,首先要了解下面幾個比較核心的類以及它的功能。
l OkHttpClient客戶端對象
l Request是OkHttp中訪問的請求,Post請求中需要包含RequestBody
l Builder是輔助類,用于生產對象
l Response即OkHttp中的響應,響應中可以得到返回是否成功,返回數據
l MediaType 數據類型,用來表明是JSON等一系列格式
l RequestBody請求數據,在Post請求中用到
l client.newCall(request).execute()是同步的請求方法
l client.newCall(request).enqueue(CallbackcallBack)是異步的請求方法,但是Callback里面的代碼是執行在子線程的,因此不能更新UI。
3、OKHttp的基本使用步驟(以POST方式從服務器取JSON數據為例)
l 創建OkHttpClient對象,官方文檔要求我們最好使用單例模式,在后文對OKHttp進行封裝的時候會提到。
l 如果是post請求的話,需要通過FormEncodingBuilder創建RequestBody對象,指定需要post傳進去的參數。get請求則不用。
l 創建Request對象,這個對象是請求對象,需要指定URL。post請求的時候需要指定RequestBody對象,get請求則不用。
l 調用OkHttpClient的newCall方法,把Request對象傳進去,然后執行execute或者enqueue方法,兩者的區別在上文已提到。在CallBack中的onResponse方法就可以做你需要做的事。onResponse回調的參數是response,一般情況下,比如我們希望獲得返回的字符串,可以通過response.body().string()獲取;如果希望獲得返回的二進制字節數組,則調用response.body().bytes();如果你想拿到返回的inputStream,則調用response.body().byteStream()看到這,你可能會奇怪,竟然還能拿到返回的inputStream,看到這個最起碼能意識到一點,這里支持大文件下載,有inputStream我們就可以通過IO的方式寫文件。不過也說明一個問題,這個onResponse執行的線程并不是UI線程。的確是的,如果你希望操作控件,還是需要使用handler等。
OkHttpClientclient = new OkHttpClient();
RequestBodybody = new FormEncodingBuilder()
.add("type", "1")
.build();
Requestrequest = new Request.Builder()
.url(Constants.URL_BANNER)
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
}
@Override
public void onResponse(Response response) throws IOException{
if (response.isSuccessful()) {
android.os.Message msg = new Message();
msg.what = 1;
msg.obj = response.body().string();
mHandler.sendMessage(msg);
}
}
});
這里只是一個簡單post請求,從服務器獲取數據的介紹,至于get請求只不過是去掉RequestBody對象而已。至于如何向服務器提交數據,可以在熟悉上面的基本用法之后查閱官方的WIKI(GitHub的介紹文檔)。另外楠妹妹也推薦下面這一篇文章,http://blog.csdn.net/lmj623565791/article/details/47911083。
四、OKHttp的簡單封裝
回顧上面的代碼,試想一下如果每次請求都寫這么多重復代碼,這樣會嚴重降低開發效率,因此需要對OKHttp進行封裝。對代碼進行封裝是我們最為面向對象程序員的基本素養,減少重復代碼,降低維護難度以及成本。
GitHub上也有對OKHttp進行過封裝,叫做OKHttpUtils。不過這里我們自己動手,一起來學習一下如何進行封裝。具體的注意點有下面幾點:
1、首先,OKHttp官方要求我們最好用單例模式去使用OKHttpClient類的,因此我們自定義一個OKHttpHelper類,并且使用單例模式。
2、對get以及post方法進行封裝,主要的思想是把共同的代碼抽取出來,例如代碼中被抽取出來的request方法。
3、對外公開一些靜態方法,包括get和post方法等。
4、Callback基類,對OKHttp的回調進行封裝。這個類用里面有一個type,是方便回調中使用Gson對JSON進行解析的封裝。使用Callback的時候只需要在泛型中傳入類似Data、List<Data>即可以方便地使用JSON。
5、由于原來的回調不在主線程,因此我們需要使用Handler來將回調放入主線程。
其余的可以參照代碼,有詳細注釋。
import android.os.Handler;
import android.os.Looper;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 這個類用來輔助OKHttp
*/
public class OkHttpHelper {
/**
* 采用單例模式使用OkHttpClient
*/
private static OkHttpHelper mOkHttpHelperInstance;
private static OkHttpClient mClientInstance;
private Handler mHandler;
private Gson mGson;
/**
* 單例模式,私有構造函數,構造函數里面進行一些初始化
*/
private OkHttpHelper() {
mClientInstance = newOkHttpClient();
mClientInstance.setConnectTimeout(10, TimeUnit.SECONDS);
mClientInstance.setReadTimeout(10, TimeUnit.SECONDS);
mClientInstance.setWriteTimeout(30, TimeUnit.SECONDS);
mGson = new Gson();
mHandler = new Handler(Looper.getMainLooper());
}
/**
* 獲取實例
*
* @return
*/
public static OkHttpHelper getinstance() {
if (mOkHttpHelperInstance == null) {
synchronized (OkHttpHelper.class) {
if (mOkHttpHelperInstance == null) {
mOkHttpHelperInstance = newOkHttpHelper();
}
}
}
return mOkHttpHelperInstance;
}
/**
* 封裝一個request方法,不管post或者get方法中都會用到
*/
public void request(final Request request, final BaseCallback callback) {
//在請求之前所做的事,比如彈出對話框等
callback.onRequestBefore();
mClientInstance.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
//返回失敗
callbackFailure(request, callback, e);
}
@Override
public void onResponse(Response response) throws IOException{
if (response.isSuccessful()) {
//返回成功回調
String resString = response.body().string();
if (callback.mType == String.class) {
//如果我們需要返回String類型
callbackSuccess(response, resString, callback);
} else {
//如果返回的是其他類型,則利用Gson去解析
try {
Object o = mGson.fromJson(resString, callback.mType);
callbackSuccess(response, o, callback);
} catch (JsonParseException e) {
e.printStackTrace();
callbackError(response, callback, e);
}
}
} else {
//返回錯誤
callbackError(response, callback, null);
}
}
});
}
/**
* 在主線程中執行的回調
*
* @param response
* @param resString
* @param callback
*/
private void callbackSuccess(final Response response, final Object o, finalBaseCallback callback) {
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onSuccess(response, o);
}
});
}
/**
* 在主線程中執行的回調
* @param response
* @param callback
* @param e
*/
private void callbackError(final Response response, final BaseCallback callback, final Exception e) {
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onError(response, response.code(), e);
}
});
}
/**
* 在主線程中執行的回調
* @param request
* @param callback
* @param e
*/
private void callbackFailure(final Request request, final BaseCallback callback, final Exception e) {
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onFailure(request, e);
}
});
}
/**
* 對外公開的get方法
*
* @param url
* @param callback
*/
public void get(String url, BaseCallback callback) {
Request request = buildRequest(url, null,HttpMethodType.GET);
request(request, callback);
}
/**
* 對外公開的post方法
*
* @param url
* @param params
* @param callback
*/
public void post(String url, Map<String, String> params, BaseCallback callback) {
Request request = buildRequest(url, params, HttpMethodType.POST);
request(request, callback);
}
/**
* 構建請求對象
*
* @param url
* @param params
* @param type
* @return
*/
private Request buildRequest(String url, Map<String, String> params, HttpMethodType type) {
Request.Builder builder = new Request.Builder();
builder.url(url);
if (type == HttpMethodType.GET) {
builder.get();
} else if (type == HttpMethodType.POST) {
builder.post(buildRequestBody(params));
}
return builder.build();
}
/**
* 通過Map的鍵值對構建請求對象的body
*
* @param params
* @return
*/
private RequestBody buildRequestBody(Map<String, String> params) {
FormEncodingBuilder builder = newFormEncodingBuilder();
if (params != null) {
for (Map.Entry<String, String> entity : params.entrySet()) {
builder.add(entity.getKey(), entity.getValue());
}
}
return builder.build();
}
/**
* 這個枚舉用于指明是哪一種提交方式
*/
enum HttpMethodType {
GET,
POST
}
}
package com.nan.cnshop.http;
import com.google.gson.internal.$Gson$Types;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* 基本的回調
*/
public abstract class BaseCallback<T> {
/**
* type用于方便JSON的解析
*/
public Type mType;
/**
* 把type轉換成對應的類,這里不用看明白也行。
*
* @param subclass
* @return
*/
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missingtype parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
/**
* 構造的時候獲得type的class
*/
public BaseCallback() {
mType = getSuperclassTypeParameter(getClass());
}
/**
* 請求之前調用
*/
public abstract void onRequestBefore();
/**
* 請求失敗調用(網絡問題)
*
* @param request
* @param e
*/
public abstract void onFailure(Request request, Exception e);
/**
* 請求成功而且沒有錯誤的時候調用
*
* @param response
* @param t
*/
public abstract void onSuccess(Response response, T t);
/**
* 請求成功但是有錯誤的時候調用,例如Gson解析錯誤等
*
* @param response
* @param errorCode
*@param e
*/
public abstract void onError(Response response, int errorCode, Exception e);
}
五、OKHttp封裝之后的使用
如下面的代碼所示。首先得到OkHttpHelper的單例,然后調用get方法就可以了。由于繼承了Gson,因此需要在BaseCallback的泛型中傳入JSON對應的數據類型,筆者這里是List<Banner>。最后在onSuccess方法中做我們想要做的事情就可以了。
mHttpHelper=OkHttpHelper.getinstance();
mHttpHelper.get(Constants.URL_BANNER, new BaseCallback<List<Banner>>() {
@Override
public void onRequestBefore() {
}
@Override
public void onFailure(Request request, Exception e) {
}
@Override
public void onSuccess(Response response, List<Banner> banners) {
initBanners(banners);
}
@Override
public void onError(Response response, int errorCode, Exception e) {
}
});
是不是覺得封裝之后OKHttp的使用變得很簡單呢,這就是封裝的強大之處,好了今天的筆記到此為止,明天見O(∩_∩)O~~。
PS:這里只介紹了OKHttp的get和post使用介紹,其余的使用例如文件下載上傳以及對應的代碼封裝請自己去完成~\(≧▽≦)/~啦啦啦。
作者: 淺若清風 時間: 2016-5-21 11:39
搶沙發,樓下做板凳
歡迎光臨 (http://www.raoushi.com/bbs/) |
Powered by Discuz! X3.1 |