編輯:關於Android編程
之前在討論組裡聽到許多討論okhttp的話題,可見okhttp是一個相對成熟的解決方案,看到android4.4後網絡訪問的源碼中HttpURLConnection已經替換成okhttp實現了,所以當時考慮來支持下,最近項目中新版應用效果良好,所以抽空來談談,順便記錄下.Volley源碼解析,Okhttp介紹,Rxjava組合.
1.使用 OkHttp 作為傳輸層的實現優點 支持SPDY(speedy,一種開放的網絡傳輸協議,用來發送網頁內容,基於傳輸控制協議(TCP)的應用層協議,SPDY協議通過壓縮、多路復用和優先級來縮短加載時間,SPDY當前並不是一個標准協議,但SPDY的開發組已經開始推動SPDY成為正式標准(現為互聯網草案[3]),HTTP/2主要以SPDY技術為主),最主要一點允許連接同一主機的所有請求分享一個socket。如果SPDY不可用,會使用連接池減少請求延遲。使用GZIP壓縮下載內容,且壓縮操作對用戶是透明的。利用響應緩存來避免重復的網絡請求。當網絡出現問題的時候,OKHttp會依然有效,它將從常見的連接問題當中恢復。如果你的服務端有多個IP地址,當第一個地址連接失敗時,OKHttp會嘗試連接其他的地址,這對IPV4和IPV6以及寄宿在多個數據中心的服務而言,是非常有必要的,OkHttp還處理了代理服務器問題和SSL握手失敗問題。2.明確一點okhttp是與HttpClient,
3.改善思路
使用 OkHttp 無需重寫您程序中的網絡代碼。OkHttp實現了幾乎和java.net.HttpURLConnection一樣的API。如果用了 Apache HttpClient,則OkHttp也提供了一個對應的okhttp-apache 模塊,OkHttp使用起來不如Volley方便,緩存機制,請求隊列管理策略,異步回調,重試機制等,OkHttp的回調都是在工作線程,所以如果在回調裡面操作View的話,需要自己轉換到UI線程,非常繁瑣,所以需要封裝. Retrofit 2.0後默認使用的是OkHttp,2.0之前是可選的,本來打算用RxJava+Retrofit,當時因為這邊從早期都是基於volley基礎來實現的,包括後面根據項目需求,對volley進行了二次封裝,由於熟悉volley的源碼,底層設計,所以繼續基於當前庫進行擴展,順便打算加入RxJava進行線程調度
上面是Volley的設計圖,相比使用過的都不陌生.RequestQueue類作為volley的核心類,可以說是連接請求與響應的橋梁,啟動一個請求隊列RequestQueue,只需要往這個RequestQueue不斷 add Request 即可,
我們可以基於自己業務需求繼承Request接口實現,ResultMapRequest
/**
* OkHttp backed {@link HttpStack HttpStack} that does not
* use okhttp-urlconnection
* OkHttp的執行器,可用於替換原框架自帶的HttpUrlConnection執行器
* 參考: https://gist.github.com/bryanstern/4e8f1cb5a8e14c202750
* https://gist.github.com/ceram1/8254f7a68d81172c1669
*/
public class OkHttpStack implements HttpStack {
private final OkHttpClient mClient;
/**
* Create a OkHttpStack with default OkHttpClient.
*/
public OkHttpStack() {
this.mClient=new OkHttpClient();
}
public OkHttpStack(OkHttpClient client) {
this.mClient = client;
}
@Override
public HttpResponse performRequest(Request request, Map additionalHeaders)
throws IOException, AuthFailureError {
OkHttpClient client = mClient.clone();
int timeoutMs = request.getTimeoutMs();
client.setConnectTimeout(timeoutMs, TimeUnit.MILLISECONDS);
client.setReadTimeout(timeoutMs, TimeUnit.MILLISECONDS);
client.setWriteTimeout(timeoutMs, TimeUnit.MILLISECONDS);
//根據volley的請求request,生成okhttp的builder構建器
//OkHttpUtils.get().url(url).build().execute(new CallBack());鏈式調用最後執行
//全部對應操作都在Builder中,所有操作完成後返回當前最新的builder
com.squareup.okhttp.Request.Builder okHttpRequestBuilder = new com.squareup.okhttp.Request.Builder();
okHttpRequestBuilder.url(request.getUrl());//設置url
KLog.i(request.getUrl().toString());
Map headers = request.getHeaders();//添加request的header
for (final String name : headers.keySet()) {
okHttpRequestBuilder.addHeader(name, headers.get(name));
}
//添加額外自定義header
for (final String name : additionalHeaders.keySet()) {
okHttpRequestBuilder.addHeader(name, additionalHeaders.get(name));
}
//根據volley請求的request,設置對應的builder,請求類型
setConnectionParametersForRequest(okHttpRequestBuilder, request);
//執行後得到相應response
com.squareup.okhttp.Request okHttpRequest = okHttpRequestBuilder.build();
Call okHttpCall = client.newCall(okHttpRequest);//不能被執行2次,每次new
Response okHttpResponse = okHttpCall.execute();
//將得到的response,轉換為BasicHttpResponse返回
return entityFromOkHttpResponse(okHttpResponse);
}
private static BasicHttpResponse entityFromOkHttpResponse(Response okHttpResponse) throws IOException {
final int responseCode = okHttpResponse.code();
//先判斷相應碼,為-1,IO異常,直接拋出異常
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(parseProtocol(okHttpResponse.protocol()), okHttpResponse.code(), okHttpResponse.message());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
//生成response相應的body實體
BasicHttpEntity entity = new BasicHttpEntity();
ResponseBody body = okHttpResponse.body();
entity.setContent(body.byteStream());
entity.setContentLength(body.contentLength());
entity.setContentEncoding(okHttpResponse.header("Content-Encoding"));//從頭部獲取ContentEncoding
if (body.contentType() != null) {
entity.setContentType(body.contentType().type());
}
//設置ENTITY
response.setEntity(entity);
//遍歷響應消息頭部,將信息加入BasicHttpResponse裡面
Headers responseHeaders = okHttpResponse.headers();
for (int i = 0, len = responseHeaders.size(); i < len; i++) {
final String name = responseHeaders.name(i), value = responseHeaders.value(i);
if (name != null) {
response.addHeader(new BasicHeader(name, value));
}
}
KLog.i(response.getStatusLine());
return response;
}
@SuppressWarnings("deprecation")
private static void setConnectionParametersForRequest(com.squareup.okhttp.Request.Builder builder, Request request)
throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Request.Method.DEPRECATED_GET_OR_POST:
// Ensure backwards compatibility. Volley assumes a request with a null body is a GET.
byte[] postBody = request.getPostBody();
if (postBody != null) {
builder.post(RequestBody.create(MediaType.parse(request.getPostBodyContentType()), postBody));
}
break;
case Request.Method.GET:
builder.get();
break;
case Request.Method.DELETE:
builder.delete();
break;
case Request.Method.POST:
builder.post(createRequestBody(request));
break;
case Request.Method.PUT:
builder.put(createRequestBody(request));
break;
case Request.Method.HEAD:
builder.head();
break;
case Request.Method.OPTIONS:
builder.method("OPTIONS", null);
break;
case Request.Method.TRACE:
builder.method("TRACE", null);
break;
case Request.Method.PATCH:
builder.patch(createRequestBody(request));
break;
default:
throw new IllegalStateException("Unknown method type.");
}
}
private static ProtocolVersion parseProtocol(final Protocol p) {
switch (p) {
case HTTP_1_0:
return new ProtocolVersion("HTTP", 1, 0);
case HTTP_1_1:
return new ProtocolVersion("HTTP", 1, 1);
case SPDY_3:
return new ProtocolVersion("SPDY", 3, 1);
case HTTP_2:
return new ProtocolVersion("HTTP", 2, 0);
}
throw new IllegalAccessError("Unkwown protocol");
}
private static RequestBody createRequestBody(Request r) throws AuthFailureError {
final byte[] body = r.getBody();
if (body == null) return null;
return RequestBody.create(MediaType.parse(r.getBodyContentType()), body);
}
}
裡面有一些是我當時加的備注,在來簡單解析下,首先HttpStack,作為網絡傳輸層統一調用接口,就是在進行網絡操作的時候調用HttpStack.performRequest(Request)
入參我們發起的請求Request,最終處理返回Response,關鍵就是如何實現這個方法:
根據volley的請求request,生成okhttp的builder構建器,發送請求,處理返回結果後拋出.
我們先來了解下okhttp的簡單使用
Request request = new Request.Builder()
.url("https://github.com/gongjr")
.header("User-Agent", "OkHttp Headers.java")
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")
.build();
Response response = client.newCall(request).execute();
細心的朋友可能注意到上面使用了
Call okHttpCall = client.newCall(okHttpRequest);每次new的方式,避免其被再次調用,因為retry機制在volley中,在上一層策略處理.
此Okhttp總體設計圖,來源其他博主,對深入了解的朋友可以看看源碼解析
簡單說,okhttp庫主要是通過Diapatcher不斷從RequestQueue中取出請求(Call),根據是否已緩存調用Cache或 Network這兩類數據獲取接口之一,從內存緩存或是服務器,得請求的數據。該引擎有同步和異步請求,同步請求通過Call.execute()直接返 回當前的Response.
前面部分在任何網絡庫中都會設計到,基本和volley請求策略設計是差不多的,但是關鍵一點是callback回來是在線程裡面, 不能刷新UI,線程調度管理,在發起與回調並沒有一體化處理,單獨使用Okhttp,還是需要額外線程調度處理,所以我們還是統一使用volley管理,當然也有習慣volley的原因O(∩_∩)O~.
從OkHttpClient類的整體設計來看,它采用門面模式來。client知曉子模塊的所有配置以及提供需要的參數。client會將所有從客戶端發來的請求委派到對應的子系統去處理。在該系統中,有多個子系統、類或者類的集合。例如上面的cache、連接以及連接池相關類的集合、網絡配置相關類集合等。每個子系統都可以被客戶端直接調 用,或者被門面角色調用。子系統並不知道門面的存在,對於子系統而言,門面僅僅是另外一個客戶端而已。同時,OkHttpClient可以看作是整個框架 的上下文。
Android SQLite數據庫基本操作方法
程序的最主要的功能在於對數據進行操作,通過對數據進行操作來實現某個功能。而數據庫就是很重要的一個方面的,Android中內置了小巧輕便,功能卻很強的一個數據庫–SQLit
android網絡編程學習與實戰之旅一(上傳單個或多個文件)
1.HttpUrlConnection類概述HttpUrlConnection是一個HTTP協議的UrlConnection,用於通過web收發數據。數據可以是任意類型和
Android設計模式系列-組合模式
Android中對組合模式的應用,可謂是泛濫成粥,隨處可見,那就是View和ViewGroup類的使用。在android UI設計,幾乎所有的widget和布局類都依靠這
Android:Volley源碼解析
簡單實例Volley是一個封裝HttpUrlConnection和HttpClient的網絡通信框架,集AsyncHttpClient和Universal-Image-L