編輯:關於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裡面有一些是我當時加的備注,在來簡單解析下,首先HttpStack,作為網絡傳輸層統一調用接口,就是在進行網絡操作的時候調用HttpStack.performRequest(Request) 入參我們發起的請求Request,最終處理返回Response,關鍵就是如何實現這個方法: 根據volley的請求request,生成okhttp的builder構建器,發送請求,處理返回結果後拋出. 我們先來了解下okhttp的簡單使用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); } }
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中內置了小巧輕便,功能卻很強的一個數據庫–SQLit
1.HttpUrlConnection類概述HttpUrlConnection是一個HTTP協議的UrlConnection,用於通過web收發數據。數據可以是任意類型和
Android中對組合模式的應用,可謂是泛濫成粥,隨處可見,那就是View和ViewGroup類的使用。在android UI設計,幾乎所有的widget和布局類都依靠這
簡單實例Volley是一個封裝HttpUrlConnection和HttpClient的網絡通信框架,集AsyncHttpClient和Universal-Image-L