<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                原文鏈接 [http://www.jianshu.com/p/9e17727f31a1](http://www.jianshu.com/p/9e17727f31a1) ### 前言 公司最近新起了一個項目,對喜歡嘗鮮的我們來說,好處就是我們可以在真實的項目中想嘗試一些新技術,驗證想法。新項目對網絡框架的選取,我們存在三種方案: 1.和我們之前的項目一樣,使用Loader + HttpClient + GreenDao + Gson + Fragment,優點是可定制性強,由于使用Google家自己的Loader和LoaderManager,代碼健壯性強。缺點是整套代碼學習成本較高,使用過程中樣板代碼較多,(比如每一個Request都需要產生一個新類)2.Volley,作為Google在IO大會上得瑟過的一個網絡庫,其實不算什么新東西(2013 IO發布),使用較為簡單,請求可以取消,可以提供優先級請求,看起來還是不錯的。3.Retrofit,一款為了使請求極度簡單化的REST API Client,呼聲也很高,使用門檻幾乎是小白型。 如何選擇呢?首先干掉1,因為對新人的學習成本確實太高,如果要快速開發一個項目,高學習成本是致命的,同時使用起來樣板代碼很多。 那么如何在Volley和Retrofit中選擇呢?盡管網上有很多文章在介紹兩個框架的使用方法,而對于其原理,特別是對比分析較少,如果你手里有一個項目,如何選擇網絡模塊呢?這里將分兩篇文章從源碼的角度對比分析這兩個開源框架,希望能對你有所幫助。這是上篇,[下篇Retrofit](http://www.jianshu.com/p/07dac989272c)在這里,其中有干貨總結哦~。 需要注意的是,這兩篇文章并不會是一個入門使用的幫助文檔,建議你在看本文的時候,最好看過官方文檔和DEMO。 首先說明一下這兩個網絡框架在項目中的層次: ![](image/57416cd15322d.png) 從上圖可知,不管Volley還是Retrofit,它們都是對現有各種方案進行整合,并提供一個友好,快速開發的方案,在整合過程中,各個模塊都可以自行定制 或者替換。比如反序列化的工作,再比如HttpClient。 需要注意一點的是,這兩個開源框架都沒有實現Http棧,Http棧作為一個可替換的模塊在兩個框架中存在(Retrofit 2.0版本僅支持OkHttpClient)。Http棧在客戶端常見的實現有 apache的HttpClient 和 square的OkHttpClient,其中apache HttpClient不開源,而OkHttpClient是開源的,[GitHub地址](https://github.com/square/okhttp)。 ### Volley 源碼分析 Volley現在已經被官方放到AOSP里面,已經逐步成為Android官方推薦的網絡框架。 ### 類抽象 1. 對Http協議的抽象 Requeset 顧名思義,對請求的封裝,實現了Comparable接口,因為在Volley中是可以指定請求的優先級的,實現Comparable是為了在Request任務隊列中進行排序,優先級高的Request會被優先調度執行。NetworkResponse Http響應的封裝,其中包括返回的狀態碼 頭部 數據等。Response 給調用者返回的結果封裝,它比NetworkResponse更加簡單,只包含三個東西:數據 異常 和 Cache數據。Network 對HttpClient的抽象,接受一個Request,返回一個NetworkResponse 1. 反序列化抽象 所謂反序列化,就是將網絡中傳輸的對象變成一個Java對象,Volley中是通過擴展Request類來實現不同的反序列化功能,如JsonRequest StringRequest,我們也可以通過自己擴展一些Request子類,來實現對請求流的各種定制。 1. 請求工作流抽象 RequestQueue 用來管理各種請求隊列,其中包含有4個隊列 a) 所有請求集合,通過RequestQueue.add()添加的Request都會被添加進來,當請求結束之后刪除。 b) 所有等待Request,這是Volley做的一點優化,想象一下,我們同時發出了三個一模一樣的Request,此時底層其實不必真正走三個網絡請求,而只需要走一個請求即可。所以Request1被add之后會被調度執行,而Request2 和Request3被加進來時,如果Request1還未執行完畢,那么Request2和 Request3只需要等著Request1的結果即可。 c) 緩存隊列,其中的Request需要執行查找緩存的工作 d) 網絡工作隊列 其中的Request需要被執行網絡請求的工作 NetworkDispatcher執行網絡Request的線程,它會從網絡工作隊列中取出一個請求,并執行。Volley默認有四個線程作為執行網絡請求的線程。 CacheDispatcher執行Cache查找的線程,它會從緩存隊列中取出一個請求,然后查找該請求的本地緩存。Volley只有一個線程執行Cache任務。 ResponseDelivery請求數據分發器,可以發布Request執行的結果。 Cache對Cache的封裝,主要定義了如何存儲,獲取緩存,存取依據Request中的getCacheKey()來描述。 ### 提交請求 Volley通過RequestQueue.add(Request)來往任務隊列中增加請求: ![](image/57416e126e694.png) 一個Request被提交之后有幾個去處: 1.Set<Request<?>> mCurrentRequests對應所有請求隊列。所有調用add的Request必然都會添加到這里面來。 2.PriorityBlockingQueue<Request<?>> mNetworkQueue 對應網絡隊列。如果一個Request不需要緩存,那么add之后會被直接添加到網絡隊列中。 3.PriorityBlockingQueue<Request<?>> mCacheQueue對應緩存請求。如果一個Request需要緩存,并且當前的RequestQueue中并沒有一個Request的getCacheKey和當前Request相同(可以認為一個請求),那么加入緩存隊列,讓緩存工作線程來處理。 4.Map<String, Queue<Request<?>>> mWaitingRequests對應等待隊列。如果RequestQueue中已經有一個相同請求在處理,這里只需要將這個Request放到等待隊列中,等之前的Request結果回來之后,進行處理即可。 Volley提交任務到隊列中是不是很簡單?下面來說說優先級請求的事情吧,你可能已經注意到了,上面兩個存放需要執行任務的隊列都是PriorityBlockingQueue,前面說了Request現實了Comparable,看看這個方法: ~~~ @Override public int compareTo(Request<T> other) { Priority left = this.getPriority(); Priority right = other.getPriority(); //mSequence表示請求序列號,add時,會通過一個計數器來指定 return left == right ? this.mSequence - other.mSequence : right.ordinal() - left.ordinal(); } ~~~ 所以,如果我們的工作線程(NetworkDispatcher,CacheDispatcher)取任務時,自然會從頭部開始取。 **這里的優先級,僅僅是保證一個請求比另外一個請求先處理,而并不能保證一個高優先級請求一定會比低優先級的請求先回來** ### 緩存工作線程處理 ~~~ @Override public void run() { //初始化Cache mCache.initialize(); Request<?> request; while (true) { //阻塞 獲取一個Cache任務 request = mCacheQueue.take(); try { //已經被取消 if (request.isCanceled()) { request.finish("cache-discard-canceled"); continue; } //如果拿cache未果,放入網絡請求隊列 Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null) { request.addMarker("cache-miss"); mNetworkQueue.put(request); continue; } //緩存超時,放入網絡請求隊列 if (entry.isExpired()) { request.addMarker("cache-hit-expired"); request.setCacheEntry(entry); mNetworkQueue.put(request); continue; } //根據Cache構造Response Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); //是否超過軟過期 if (!entry.refreshNeeded()) { // 直接返回Cache mDelivery.postResponse(request, response); } else { request.setCacheEntry(entry); //設置中間結果 response.intermediate = true; //發送中間結果 final Request<?> finalRequest = request; mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { //中間結果完事之后,講請求放入網絡隊列 mNetworkQueue.put(finalRequest); } catch (InterruptedException e) { // Not much we can do about this. } } }); } } catch (Exception e) { } } } ~~~ 這里可以看到Volley確實對緩存封裝很到位,各種情況都考慮到了,其中比較重要的兩點: 1. 取出來的Cache并不僅僅是數據,同時還包括這次請求的一些Header 1. 硬過期 軟過期 我們可以看到Cache中有兩個字段來描述緩存過期: Cache.ttl vs Cache.softTtl。什么區別呢?如果ttl過期,那么這個緩存永遠不會被使用了;如果softTtl沒有過期,這個數據直接返回;如果softTtl過期,那么這次請求將有兩次返回,第一次返回這個Cahce,第二次返回網絡請求的結果。想想,這個是不是滿足我們很多場景呢?先進入頁面展示緩存,然后再刷新頁面;如果這個緩存太久了,可以等待網絡數據回來之后再展示數據,是不是很贊? ### NetworkDispatcher 執行網絡請求的工作線程,默認有4個線程,它不停地從網絡隊列中取任務執行。 ~~~ public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); Request<?> request; while (true) { long startTimeMs = SystemClock.elapsedRealtime(); // release previous request object to avoid leaking request object when mQueue is drained. request = null; try { request = mQueue.take(); } catch (InterruptedException e) { if (mQuit) { return; } continue; } try { request.addMarker("network-queue-take"); //取消 if (request.isCanceled()) { request.finish("network-discard-cancelled"); continue; } //通過Http棧實現客戶端發送網絡請求 NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete"); // 如果緩存軟過期,那么會重新走網絡;如果server返回304,表示上次之后請求結果數據本地并沒有過期,所以可以直接用本地的,因為之前Volley已經發過一次Response了,所以這里就不需要再發送Response結果了。 if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); continue; } Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); //更新緩存 if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } //發送結果 request.markDelivered(); mDelivery.postResponse(request, response); } catch (VolleyError volleyError) { volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); parseAndDeliverNetworkError(request, volleyError); } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); VolleyError volleyError = new VolleyError(e); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); mDelivery.postError(request, volleyError); } } } ~~~ ### Request Request中主要封裝了一個請求的各類Http協議信息,比如 URL,請求方法,請求的優先級,請求重試的策略,緩存策略等。 這里說一下其中比較有意思的重發策略,如果一次請求發生超時異常,比如SocketTimeoutException ConnectTimeoutException ,我們可以為Request配置一個RetryPolicy,你可以指定重發這個Request的次數,以及每次失敗之后重新設置這個請求的超時時間(第一次失敗之后,你可以調整第二次請求的超時時間增加,以減少失敗的可能性)。 ### 反序列化 Request最重要的功能就是提供了內容的反序列化,通過不同的子類來實現不同的序列化功能。比如,如果請求結果是一個Json的對象,我們可以使用JsonObjectRequest,如果是一個普通字符,使用StringRequest,同時,我們也可以很方便的定制自己的Request,通過復寫Response<T> parseNetworkResponse(NetworkResponse response);方法即可。 默認的JsonRequest使用org.json中的Json解析,我們使用Gson來進行解析能夠構造一個更加通用的處理json返回的Request: ~~~ public class JsonGRequest<T> extends Request<T> { private static Gson gson = new Gson(); private Response.Listener<T> mListener; public JsonGRequest(String url, Response.ErrorListener listener,Response.Listener responseListener) { super(url, listener); this.mListener = mListener; } public JsonGRequest(int method, String url, Response.ErrorListener listener) { super(method, url, listener); } @Override protected Response<T> parseNetworkResponse(NetworkResponse response) { return Response.success(gson.fromJson(new InputStreamReader(new ByteArrayInputStream(response.data)),getType()), HttpHeaderParser.parseCacheHeaders(response)) } @Override protected void deliverResponse(T response) { if(mListener != null) { mListener.onResponse(response); } } //獲取指定的泛型類型 protected Type getType() { Type superclass; for(superclass = this.getClass().getGenericSuperclass(); superclass instanceof Class && !superclass.equals(JsonGRequest.class); superclass = ((Class)superclass).getGenericSuperclass()) { ; } if(superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } else { ParameterizedType parameterized = (ParameterizedType)superclass; return parameterized.getActualTypeArguments()[0]; } } } ~~~ ### ImageRequest Volley專門為圖片請求提供了ImageRequest,主要是反序列化了一下數據流到BitMap,還可以制定圖片的大小,質量等參數。 ImageLoader是Volley提供的一個用來加載圖片的工具,它的內部還是使用ImageRequest來實現的,主要新加的功能是增加了內存緩存,你可以通過配置ImageCache來設置內存緩存。 小結 Volley整體代碼還是比較簡單,思路明確,而且提供了不錯的可擴展性,而且各個方面考慮得較為全面。下面我們分析一下Retrofit的源碼。 文/楚云之南(簡書作者) 原文鏈接:http://www.jianshu.com/p/9e17727f31a1
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看