本文來自http://blog.csdn.net/liuxian13183/ ,引用必須註明出處!
java
隨着項目日趨穩定,需求再也不老是變化,那麼是時間來整理下項目了。先簡單介紹下,本項目最初使用loop4j(即async-http)框架,僅98kb大小,使用也比較方便,爲何要選用它呢?13年的時候其餘框架還沒那麼成熟,我們作項目穩定第一,其次流暢,再次性能,而它恰好知足這個條件;很差的地方在於請求慢,並且回調顯得煩瑣。android
使用方法以下:ios
一、初始化請求客戶端json
private static AsyncHttpClient client; /** * 重試3次<br> * * 超時20s */ static { client = new MyAsyncHttpClient(); client.setTimeout(10 * 1000);//要設置超時間,默認的爲10s client.setMaxRetriesAndTimeout(3, 10 * 1000); client.setEnableRedirects(false); // 容許環形重定向和設置重定向最大次數。 client.getHttpClient().getParams().setParameter(ClientPNames.MAX_REDIRECTS, 3); client.getHttpClient().getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, false); }
class MyAsyncHttpClient extends AsyncHttpClient { @Override public void setEnableRedirects(final boolean enableRedirects) { ((DefaultHttpClient) getHttpClient()).setRedirectHandler(new DefaultRedirectHandler() { @Override public boolean isRedirectRequested(HttpResponse response, HttpContext context) { int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == 301 || statusCode == 302) { return enableRedirects; } return false; } }); } }二、設置返回調用
protected JsonHttpResponseHandler responseHandler = new JsonHttpResponseHandler() {
/**
* Returns when request failed
*
* @param statusCode http response status line
* @param headers response headers if any
* @param throwable throwable describing the way request failed
* @param errorResponse parsed response if any
*/
public void onFailure(int statusCode, Throwable throwable, JSONObject errorResponse) {
boolean isNetAvailable = FactoryProxy.getInstance().getNetStatusManager().isNetAvailable();
mTask.onError(DEFAULT_TASK_WHAT, isNetAvailable ? "加載失敗,請重試" : "網絡異常");
Message message = new Message();
message.what = MSG_CODE_LOAD_ERROR;
message.obj = DEFAULT_TASK_WHAT;
handler.sendMessage(message);
}
@Override
public void onSuccess(int statusCode, JSONObject response) {
super.onSuccess(statusCode, response);
setPtrFinished();
// 訪問失敗
if (statusCode != 200) {
// showToast(R.string.common_str_net_invailable);
return;
}
// 若是返回的編號小於0的話證實有錯誤
int error_no = response.optInt("erro_no");
if (error_no < 0) {
switch (error_no) {
case -102:// 異常處理
//要用到在當前頁面處理的
mTask.onError(DEFAULT_TASK_WHAT, response.optString("error_no", "暫無數據"));
break;
default:
mTask.reset();
break;
}
Message message = handler.obtainMessage(error_no, response);
handler.sendMessage(message);//主要用於toast
} else {
// 不然沒有錯誤解析數據
if (response.has(API_METHOD_DATA)) {
Object json = response.get(API_METHOD_DATA);
if (json instanceof JSONObject) {
CMYJSONObject obj = response.optBaseJSONObject(API_METHOD_DATA);
mTask.onFinish(DEFAULT_TASK_WHAT, obj);
} else if (json instanceof JSONArray) {
mTask.onFinish(DEFAULT_TASK_WHAT, response);
} else {
mTask.onFinish(DEFAULT_TASK_WHAT, response);
}
} else {
mTask.onFinish(DEFAULT_TASK_WHAT, response);
}
}
}
};
三、發起請求
protected void sendRequest(String method, LXBaseRequest request, AsyncHttpResponseHandler responseHandler) { RequestParams requestParams = FactoryProxy.getInstance().getAccountManager().getRequestParams(request); HttpBusinessAPI.post(method, requestParams, responseHandler); }
關於OkHttp是一個月前準備開工的,當時也想把項目總體框架重構一下,最大塊也是網絡請求層和邏輯處理層,其餘公用組件僅作相應的適配便可,使用初期遇到一些問題,最後改完整個項目以後,發現請求速度快了大概30%左右吧。後端
這裏先講幾個重構過程當中遇到的問題:緩存
一、是項目要求上傳JsonRequest而非JsonString,而demo和網上也可能是基於JsonString,這時兩個解決辦法,一個是要求後端改上傳數據的要求,一種就是本身修改,跟後端溝通中也提出上傳參數效率的問題,但項目比較大並且涉及到ios方面改動太大而做罷,最終使用下面的方案解決-使用對象格式安全
FormBody.Builder builder = new FormBody.Builder(); for (Iterator<String> iterator = params.keys(); iterator.hasNext(); ) { String key = iterator.next().toString(); String value = params.optString(key); builder.add(key, value); } FormBody body = builder.build(); Request request = new Request.Builder() .url(url) .post(body) .build();
下面是網上的示例-使用Json數據格式服務器
RequestBody body = RequestBody.create(JSON, json); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute();同時這個問題的解決還有個小插曲,關於數據格式的,OkHttp默認使用application/x-www-form-urlencoded默認是鍵值對;而另一種multipart/form-data通常用來傳輸圖片,接下來會講到;最後一種是text/plain主要傳輸文字;固然還有不少其餘類型,而這三種最多見。最初想更換content-type來實現,最終也沒有實現。
二、項目要求有圖片上傳功能,而圖片格式多種多樣,如何實現呢,okhttp也沒提供現成的方法可用,那就逐步撕源碼吧,上傳圖片第一要考慮文本類型,第二Content-type,最後以什麼數據封裝,答案是使用image/*包含全部圖片,第二設置爲Form類型,最後以FormDataPart的形式封裝,代碼以下網絡
CMYJSONObject object = CMYApplication.getInstance().getAccountManager().getJsonParams(null); //參數類型 MediaType MEDIA_TYPE_IMAGE = MediaType.parse("image/*"); MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM); for (Iterator<String> iterator = object.keys(); iterator.hasNext(); ) { String key = (String) iterator.next(); builder.addFormDataPart(key, object.optString(key)); } File file = new File(params[1]); builder.addFormDataPart(params[0], file.getName(), RequestBody.create(MEDIA_TYPE_IMAGE, file)); //構建請求體 RequestBody body = builder.build(); Request buildRequest = new Request.Builder() .url(url) .post(body) .build(); client.newCall(buildRequest).enqueue(callback);三、在決定用同步仍是異步的時候 ,發現同步須要每次開一個線程,好比用AsyncTask去處理,但對於整個框架就須要寫多少這樣的task類呀,所以考慮用異步+ViewThread的方法,最終請求結果做用於View;固然同步也有一個自然好處,就是不用判斷當前callback返回的數據是否是本身view的。所以異步線程,整體仍是同步的,當前頁面一個執行完才能再執行另一個;而同步線程反而能夠同時開啓多個,不用擔憂返回結果,由於它是直接拿到的,不用靠callback;整體上來講,網絡的請求速度來講,硬件給予的網速已經限定,除非你要佔用全網速,這個就跟前面「併發激發處理器的所有工做能力」是同樣的道理,必定程度來請多線程能夠加速獲得咱們想要的結果,但不是絕對的。
四、okhttp的超時問題,真的頭疼,由於第一callback不管onResponse仍是onFailure都要throws IOException,第二網絡超時+無網絡,第三其餘錯誤;固然本次只請網絡問題,其餘兩種屬於框架層次的問題;不像loop4j能夠設置超時從新請求和次數,而okhttp僅能設置讀(Response)、寫(Request)、鏈接(Link)的超時時間,因此請求失敗的次數天然會比loop4j多一些,怎麼解決呢,設置網絡鏈接時間最長,60s。session
上面均是post請求,下面給出get請求的兩個案例,一個同步一個異步
Request buildRequest = new Request.Builder().url(url).build(); Response response = client.newCall(buildRequest).execute(); if (response.isSuccessful()) { return response.body().string(); } else { throw new IOException("Unexpected code " + response); }
Request buildRequest = new Request.Builder() .url(url) .build(); client.newCall(buildRequest).enqueue(callback);
最後爲何採用OkHttp,由於它支持HttpUrlConnection(Android系統層作了優化),而Android已然放棄HttpClient(Apache開源);最重要的是android底層的網絡請求已經使用okhttp(在打log時發現),而loop4j是典型的HttpClient使用者;而前者目前的優點還不是很明顯,如後面的優點非常明顯;就像如今轉向AS開發工具同樣,相信它會愈來愈好,成爲愈來愈專業的Android開發工具。
介紹完okhttp總以爲還缺點什麼,再類比下跟其餘框架的區別吧
先說最古老的那種,使用HttpClient,傳入url,設置相關超時、content-type等屬性後,得到返回結果;後面的都是在這個基礎之上(原理),加強功能
public class HttpClientConnector { public static String getStringByUrl(String url) { String outputString = ""; // DefaultHttpClient DefaultHttpClient httpclient = new DefaultHttpClient(); // HttpGet HttpGet httpget = new HttpGet( url); // 連接超時 httpclient.getParams().setParameter( CoreConnectionPNames.CONNECTION_TIMEOUT, 6000); // 讀取超時 httpclient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 10000); // ResponseHandler ResponseHandler<String> responseHandler = new BasicResponseHandler(); try { outputString = httpclient.execute(httpget, responseHandler); String replacement = "www.book.com.cn"; switch (WholeMagDatas.netType) { case 1: replacement = "www.book.com.cn"; break; case 2: replacement = "www1.book.com.cn"; break; case 3: replacement = "www2.book.com.cn"; break; default: replacement = "www.book.com.cn"; break; } outputString.replaceAll("www.book.com.cn", replacement); // Log.i(WholeMagConstants.APP_NAME, "鏈接成功"); } catch (Exception e) { // Log.i(WholeMagConstants.APP_NAME, "鏈接失敗"); // httpget = new HttpGet(WholeMagDatas.WMSERVER_BASE_URL2+url); // try { // outputString = httpclient.execute(httpget, responseHandler); // } catch (ClientProtocolException e1) { // // TODO Auto-generated catch block // e1.printStackTrace(); // } catch (IOException e1) { // // TODO Auto-generated catch block // e1.printStackTrace(); // } e.printStackTrace(); } httpclient.getConnectionManager().shutdown(); Log.i(AppData.WM_LOG_HOME, "outputString:" + outputString); return outputString; } }
框架最主要的改動還在於加強請求的安全性和請求速度,另外還有支持豐富的數據上傳和接收。
okhttp,支持同步和異步請求,以及不一樣的數據如xml或json或string或其餘數據的傳輸,固然能夠設置請求頭,跟上面無異,主要在於優化了加載過程,所以加載速度更快,同時能夠取消請求,支持session的保持;支持http/2和spdy,鏈接池,gziping
(經測試AsnycHttp的get請求最大傳輸數據量爲8k。
OkHttp的get請求最大傳輸數據量爲16M,再大會說頭文件過大。設置太大的數據每每出現下面錯誤。
java.lang.OutOfMemoryError: Failed to allocate a 268435468 byte allocation with 16777120 free bytes and 93MB until OOM)
雖說get數據通常http請求都會限制,但封裝、壓縮後的數據,能夠傳輸更多。
volly,基本跟okhttp差很少,但支持的數據豐富度和優化程度不如okhttp
其餘暫不作介紹,由於google已經默認okhttp爲底層網絡請求框架,之後也會作的更好吧。
經測試AsnycHttp的get請求最大傳輸數據量爲8k。
OkHttp的get請求最大傳輸數據量爲16M
java.lang.OutOfMemoryError: Failed to allocate a 268435468 byte allocation with 16777120 free bytes and 93MB until OOM
順便講下HttpConnections與HttpClient的差異;簡而言之,前者是後者的升級版本。
一、在系統層作了緩存策略,加快請求速度
二、直接支持gzip數據壓縮包(服務端也要支持)-Accept-Encoding: gzip
三、鏈接池不會主動關閉,支持多程序共用,如關閉須要調用disconnect方法
四、設計HttpResponseCache,作數據請求緩存,減小服務器壓力
long httpCacheSize = 10 * 1024 * 1024;// 10M File httpCacheDir = new File(getCacheDir(), "http"); Class.forName("android.net.http.HttpResponseCache") .getMethod("install", File.class, long.class) .invoke(null, httpCacheDir, httpCacheSize);
new Request.Builder().cacheControl(CacheControl.FORCE_CACHE)五、所以,請求速度是HttpClient的幾倍