1. 程式人生 > >Android應用開發:網絡工具——Volley(二)

Android應用開發:網絡工具——Volley(二)

respond sid 開發 多少 called creat miss 相等 eal

引言

Android應用開發:網絡工具——Volley(一)中結合Cloudant服務介紹了Volley的一般使用方法,當中包括了兩種請求類型StringRequest和JsonObjectRequest。一般的請求任務相信都能夠通過他們完畢了,只是在千變萬化的網絡編程中。我們還是希望能夠對請求類型、過程等步驟進行全然的把控,本文就從Volley源代碼角度來分析一下,一個網絡請求在Volley中是怎樣運作的。也能夠看作網絡請求在Volley中的生命周期。


源頭RequestQueue


在使用Volley前,必須有一個網絡請求隊列來承載請求。所以先分析一下這個請求隊列是怎樣申請。假設運作的。

在Volley.java中:

    /**
      * Creates a default instance of the worker pool and calls [email protected] RequestQueue#start()} on it.
      *
      * @param context A [email protected] Context} to use for creating the cache dir.
      * @param stack An [email protected] HttpStack} to use for the network, or null for default.
      * @return A started [email protected]
/* */ RequestQueue} instance. */ public static RequestQueue newRequestQueue(Context context, HttpStack stack) { File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); String userAgent = "volley/0"; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } if (stack == null) { if (Build.VERSION.SDK_INT >= 9) { stack = new HurlStack(); } else { // Prior to Gingerbread, HttpUrlConnection was unreliable. // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } Network network = new BasicNetwork(stack); RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); queue.start(); return queue; } /** * Creates a default instance of the worker pool and calls [email protected]
/* */ RequestQueue#start()} on it. * * @param context A [email protected] Context} to use for creating the cache dir. * @return A started [email protected] RequestQueue} instance. */ public static RequestQueue newRequestQueue(Context context) { return newRequestQueue(context, null); }

通常使用的是第二個接口。也就是僅僅有一個參數的newRequestQueue(Context context),使stack默覺得null。

能夠看到我們得到的RequestQueue是通過RequestQueue申請。然後又調用了其start方法,最後返回給我們的。接下來看一下RequestQueue的構造方法:

     /**
      * Creates the worker pool. Processing will not begin until [email protected] #start()} is called.
      *
      * @param cache A Cache to use for persisting responses to disk
      * @param network A Network interface for performing HTTP requests
      * @param threadPoolSize Number of network dispatcher threads to create
      * @param delivery A ResponseDelivery interface for posting responses and errors
      */
     public RequestQueue(Cache cache, Network network, int threadPoolSize,
             ResponseDelivery delivery) {
         mCache = cache;
         mNetwork = network;
         mDispatchers = new NetworkDispatcher[threadPoolSize];
         mDelivery = delivery;
     }

     /**
      * Creates the worker pool. Processing will not begin until [email protected] #start()} is called.
      *
      * @param cache A Cache to use for persisting responses to disk
      * @param network A Network interface for performing HTTP requests
      * @param threadPoolSize Number of network dispatcher threads to create
      */
     public RequestQueue(Cache cache, Network network, int threadPoolSize) {
         this(cache, network, threadPoolSize,
                 new ExecutorDelivery(new Handler(Looper.getMainLooper())));
     }

     /**
      * Creates the worker pool. Processing will not begin until [email protected] #start()} is called.
      *
      * @param cache A Cache to use for persisting responses to disk
      * @param network A Network interface for performing HTTP requests
      */
     public RequestQueue(Cache cache, Network network) {
         this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
     }
RequestQueue有三種構造方法,通過newRequestQueue(Context context)調用的是最後一種。創建了一個工作池,默認承載網絡線程數量為4個。而後兩種構造方法都會調用到第一個。進行了一些局部變量的賦值。並沒有什麽須要多說的,接下來看start()方法:

     public void start() {
         stop();  // Make sure any currently running dispatchers are stopped.
         // Create the cache dispatcher and start it.
         mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
         mCacheDispatcher.start();

         // Create network dispatchers (and corresponding threads) up to the pool size.
         for (int i = 0; i < mDispatchers.length; i++) {
             NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                     mCache, mDelivery);
             mDispatchers[i] = networkDispatcher;
             networkDispatcher.start();
         }
     }

首先進行了stop操作,將所有的運行者所有退出,從而確保當前沒有不論什麽正在工作的運行者。然後基本的工作就是開啟一個CacheDispatcher和符合線程池數量的NetworkDispatcher。首先分析CacheDispatcher。


CacheDispatcher緩存操作


CacheDispatcher為緩存隊列處理器。創建伊始就被責令開始工作start()。由於CacheDispatcher繼承於Thread類,所以須要看一下它所復寫的run方法:

     @Override
     public void run() {
         if (DEBUG) VolleyLog.v("start new dispatcher");
         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

         // Make a blocking call to initialize the cache.
         mCache.initialize(); //初始化一個緩存

         while (true) {
             try {
                 // Get a request from the cache triage queue, blocking until
                 // at least one is available.
                 final Request<?> request = mCacheQueue.take(); //在緩存序列中獲取請求,堵塞操作
                 request.addMarker("cache-queue-take");

                 // If the request has been canceled, don‘t bother dispatching it.
                 if (request.isCanceled()) { //若該請求已經被取消了,則直接跳過
                     request.finish("cache-discard-canceled");
                     continue;
                 }

                 // Attempt to retrieve this item from cache.
                 Cache.Entry entry = mCache.get(request.getCacheKey()); //嘗試在緩存中查找是否有緩存數據
                 if (entry == null) {
                     request.addMarker("cache-miss"); //若沒有則緩存丟失,證明這個請求並沒有獲得實施過。扔進網絡請求隊列中
                     // Cache miss; send off to the network dispatcher.
                     mNetworkQueue.put(request);
                     continue;
                 }

                 // If it is completely expired, just send it to the network.
                 if (entry.isExpired()) { //若請求已經過期,那麽就要去獲取最新的消息。所以依舊丟進網絡請求隊列中
                     request.addMarker("cache-hit-expired");
                     request.setCacheEntry(entry);
                     mNetworkQueue.put(request);
                     continue;
                 }

                 // We have a cache hit; parse its data for delivery back to the request.
                 request.addMarker("cache-hit");
                 Response<?

> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); //請求有緩存數據且沒有過期,那麽能夠進行解析,交給請求的parseNetworkReponse方法進行解析。這種方法我們能夠在自己定義個Request中進行復寫自己定義 request.addMarker("cache-hit-parsed"); if (!entry.refreshNeeded()) { //假設請求有效且並不須要刷新,則丟進Delivery中處理,終於會觸發如StringRequest這種請求子類的onResponse或onErrorResponse // Completely unexpired cache hit. Just deliver the response. mDelivery.postResponse(request, response); } else { //請求有效,可是須要進行刷新,那麽須要丟進網絡請求隊列中 // Soft-expired cache hit. We can deliver the cached response, // but we need to also send the request to the network for // refreshing. request.addMarker("cache-hit-refresh-needed"); request.setCacheEntry(entry); // Mark the response as intermediate. response.intermediate = true; // Post the intermediate response back to the user and have // the delivery then forward the request along to the network. mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { mNetworkQueue.put(request); } catch (InterruptedException e) { // Not much we can do about this. } } }); } } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } } }

CacheDispatcher做了非常多事情,之後再來慢慢的消化他們。如今先看一下我們的請求通過add之後到了哪裏去。

查看RequestQueue.java的add方法:

     /**
      * Adds a Request to the dispatch queue.
      * @param request The request to service
      * @return The passed-in request
      */
     public <T> Request<T> add(Request<T> request) {
         // Tag the request as belonging to this queue and add it to the set of current requests.
         request.setRequestQueue(this);
         synchronized (mCurrentRequests) {
             mCurrentRequests.add(request); //增加到當前的隊列中,是一個HashSet
         }

         // Process requests in the order they are added.
         request.setSequence(getSequenceNumber());
         request.addMarker("add-to-queue");

         // If the request is uncacheable, skip the cache queue and go straight to the network.若這個請求不須要被緩存,須要直接做網絡請求,那麽就直接加到網絡請求隊列中
         if (!request.shouldCache()) {
             mNetworkQueue.add(request);
             return request;
         }

         // Insert request into stage if there‘s already a request with the same cache key in flight.
         synchronized (mWaitingRequests) {
             String cacheKey = request.getCacheKey(); // Volley中使用請求的URL作為存儲的key
             if (mWaitingRequests.containsKey(cacheKey)) { //若等待的請求中有與所請求的URL同樣的請求,則須要做層級處理
                 // There is already a request in flight. Queue up.
                 Queue<Request<?

>> stagedRequests = mWaitingRequests.get(cacheKey); if (stagedRequests == null) { stagedRequests = new LinkedList<Request<?

>>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); //若與已有的請求URL同樣,則創建一個層級列表保存他們。然後再放入等待請求列表中 if (VolleyLog.DEBUG) { VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); } } else { // Insert ‘null‘ queue for this cacheKey, indicating there is now a request in // flight. mWaitingRequests.put(cacheKey, null); //若是一個全新的請求,則直接放入等待隊列中,註意數據為null,僅僅有多個url產生層級關系了才有數據 mCacheQueue.add(request); //放入緩存隊列中。緩存隊列會對請求做處理 } return request; } }


這裏的mCacheQueue就是放入CacheDispatcher的那個堵塞隊列,所以在add中加入到mCacheQueue後。由於CacheDispatcher已經執行起來了,所以CacheDispatcher會對剛剛加入的網絡請求做處理。分析到這裏,能夠進行一下階段性的梳理:

1. 我們的請求在增加到RequestQueue後,首先會增加到事實上體類的mCurrentRequests列表中做本地管理

2. 假設之前已經存在了和本次請求相同URL的請求,那麽會將層級關系保存在mWaitingRequests中。若沒有則層級關系為null,相同也會保存在mWaitingRequests中

3. 對於沒有層級關系(新的URL)的網絡請求會直接放入mCacheQueue中讓CacheDispatcher對其進行處理

分析到這裏發現對於同一個URL的請求處理比較特殊,當第一次做某個網絡請求A時候,A會直接放入緩存隊列中由CacheDispatcher進行處理。下一次進行同一個URL的請求B時,若此時A還存在於mWaitingRequests隊列中則B的請求被雪藏,不放入mCacheQueue緩存隊列進行處理,僅僅是等待。那麽等待到什麽時候呢?不難猜想到是須要等待A的請求完成後才幹夠進行B的請求。歸結究竟就是須要知道mWaitingRequest是怎樣運作的?什麽時候存儲在當中的層級結構才會被拿出來進行請求。臨時記下這個問題,如今回頭再去繼續分析CacheDispatcher。

CacheDispatcher對請求的處理能夠歸結為下面幾種情況:


1. 對於取消的請求,直接表示為完畢並跳過。

2. 對於尚未有應答數據的、數據過期、有明顯標示須要刷新的請求直接丟入mNetworkQueue。mNetworkQueue同mCacheQueue一樣,是一個堵塞隊列;

3. 對於有應答數據且數據尚未過期的請求會出發Request的parseNetworkResponse方法進行數據解析,這種方法能夠通過繼承Request類進行復寫(定制);

4. 對於有效應答(不管是否須要更新)都會用mDelivery進行應答,須要刷新的請求則會再次放入到mNetworkQueue中去。

對於(1)暫不做分析,後邊會遇到。

下邊分析一下mNetworkQueue的運作原理,mNetworkQueue是在CacheDispatcher構造時傳入的參數。通過RequestQueue的start()方法不難分析出相相應的處理器為NetworkDispatcher。


NetworkDispatcher網絡處理

在RequestQueue的start()方法中,NetworkDispatcher存在多個。其數量等於RequestQueue構造時候傳入的網絡處理線程數量相等,默覺得4個。

    public void start() {
        stop();  // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it.
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        // Create network dispatchers (and corresponding threads) up to the pool size.
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

每個dispatcher被創造後都及時進行了start()操作。而NetworkDispatcher也是繼承於Thread的類,那麽之後須要分析其復寫的run方法,在這之前先看一下它的構造方法:

    public NetworkDispatcher(BlockingQueue<Request<?>> queue,
            Network network, Cache cache,
            ResponseDelivery delivery) {
        mQueue = queue;
        mNetwork = network;
        mCache = cache;
        mDelivery = delivery;
    }
mQueue即為mNetworkQueue,這與CacheDispatcher中使用到的是同一個。

而mNetwork默認是BasicNetwork。mCache為緩存。mDelivery為終於的消息配發者,之後會分析到。

接下來看其復寫的run()方法:

    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //設置線程可後臺執行,不會由於系統休眠而掛起
        Request<?

> request; while (true) { try { // Take a request from the queue. request = mQueue.take(); //mQueue即為mNetworkQueue,從mNetworkQueue中獲取請求,也就是說CacheDispatcher丟過來的請求是從這裏被NetworkDispatcher獲取到的。

註意這裏獲取請求是堵塞的。 } catch (InterruptedException e) { //退出操作,NetworkDispatcher被設置成退出時候發出中斷請求 // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { request.addMarker("network-queue-take"); // If the request was cancelled already, do not perform the // network request. if (request.isCanceled()) { //若請求已經被取消。則標記為完畢(被取消),然後繼續下一個請求 request.finish("network-discard-cancelled"); continue; } addTrafficStatsTag(request); // Perform the network request. NetworkResponse networkResponse = mNetwork.performRequest(request); //使用BasicNetwork處理請求 request.addMarker("network-http-complete"); // If the server returned 304 AND we delivered a response already, // we‘re done -- don‘t deliver a second identical response. if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); continue; } // Parse the response here on the worker thread. Response<?

> response = request.parseNetworkResponse(networkResponse); //處理網絡請求應答數據 request.addMarker("network-parse-complete"); // Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for 304s. if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } // Post the response back. request.markDelivered(); //標記請求為已應答並做消息分發處理 mDelivery.postResponse(request, response); } catch (VolleyError volleyError) { parseAndDeliverNetworkError(request, volleyError); //若產生Volley錯誤則會觸發Request的parseNetworkError方法以及mDelivery的postError方法 } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); mDelivery.postError(request, new VolleyError(e)); //對於未知錯誤,僅僅會觸發mDelivery的postError方法。 } } }

mNetwork.performRequest是真正的網絡請求實施的地方,這裏對BasicNetwork不做分析。網絡請求的回應是NetworkResponse類型,看一下這個類型是怎麽樣的:

/**
  * Data and headers returned from [email protected] Network#performRequest(Request)}.
  */
 public class NetworkResponse {
     /**
      * Creates a new network response.
      * @param statusCode the HTTP status code
      * @param data Response body
      * @param headers Headers returned with this response, or null for none
      * @param notModified True if the server returned a 304 and the data was already in cache
      */
     public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
             boolean notModified) {
         this.statusCode = statusCode;
         this.data = data;
         this.headers = headers;
         this.notModified = notModified;
     }

     public NetworkResponse(byte[] data) {
         this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);
     }

     public NetworkResponse(byte[] data, Map<String, String> headers) {
         this(HttpStatus.SC_OK, data, headers, false);
     }

     /** The HTTP status code. */
     public final int statusCode;

     /** Raw data from this response. */
     public final byte[] data;

     /** Response headers. */
     public final Map<String, String> headers;

     /** True if the server returned a 304 (Not Modified). */
     public final boolean notModified;
 }
NetworkResponse保存了請求的回應數據,包含數據本身和頭,還有狀態碼以及其它相關信息。依據請求類型的不同,對回應數據的處理方式也各有不同,比如回應是String和Json的差別。所以自然而然的網絡請求類型須要對它獲得的回應數據自行處理,也就觸發了Request子類的parseNetworkResponse方法。下邊以StringRequest為例進行分析:

     @Override
     protected Response<String> parseNetworkResponse(NetworkResponse response) {
         String parsed;
         try {
             parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
         } catch (UnsupportedEncodingException e) {
             parsed = new String(response.data);
         }
         return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
     }
StringRequest中對於回應首先嘗試解析數據和辨別頭數據編碼類型。若失敗則僅僅解析數據部分。終於都是觸發Request的success方法。參數中還使用Volley自帶的HttpHeaderParser對頭信息進行了解析。

須要看一下Response的success方法到底做了什麽。鑒於Response類總共沒有多少代碼,就所有拿出來做分析了:

 public class Response<T> {

     /** 處理解析過的回應信息的回調接口 */
     public interface Listener<T> {
         /** 當接收到回應後 */
         public void onResponse(T response);
     }

     /** 處理錯誤回應的回調接口 */
     public interface ErrorListener {
         /**
          * 發生錯誤時的回調接口
          */
         public void onErrorResponse(VolleyError error);
     }

     /** 返回一個包括已解析結果的成功回應 */
     public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
         return new Response<T>(result, cacheEntry);
     }

     /**
      * 返回錯誤回應,包括錯誤碼以及可能的其它消息
      */
     public static <T> Response<T> error(VolleyError error) {
         return new Response<T>(error);
     }

     /** 解析過的響應信息,錯誤時為null */
     public final T result;

     /** 響應的緩存數據,錯誤時為null */
     public final Cache.Entry cacheEntry;

     /** 具體的錯誤信息 */
     public final VolleyError error;

     /** 此回應軟件希望得到第二次回應則為true,即須要刷新 */
     public boolean intermediate = false;

     /**
      * 返回true代表回應成功。沒有錯誤。有錯誤則為false
      */
     public boolean isSuccess() {
         return error == null;
     }


     private Response(T result, Cache.Entry cacheEntry) {
         this.result = result;
         this.cacheEntry = cacheEntry;
         this.error = null;
     }

     private Response(VolleyError error) {
         this.result = null;
         this.cacheEntry = null;
         this.error = error;
     }
 }
這就是網絡響應的類,非常easy,成功或錯誤都會直接進行標記。通過isSuccess接口提供外部查詢。假設響應成功。則消息保存在result中,解析頭信息得到的緩存數據保存在cacheEntry中。

Request作為基類,Volley自帶的又代表性的其擴展類又StringRequest和JsonObjectRequest。假設開發人員有比較大的自己定義需求就須要繼承Request復寫內部一些重要的方法。

同一時候mDelivery出場的機會這麽多,為什麽他總出如今處理請求的地方呢?下邊就對它和Request一起進行分析,當中Request依舊以StringRequest為例。

ExecutorDelivery消息分發者與Request請求

mDelivery類型為ResponseDelivery,實為接口類型:

public interface ResponseDelivery {
    /**
     * Parses a response from the network or cache and delivers it.
     */
    public void postResponse(Request<?> request, Response<?> response);

    /**
     * Parses a response from the network or cache and delivers it. The provided
     * Runnable will be executed after delivery.
     */
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable);

    /**
     * Posts an error for the given request.
     */
    public void postError(Request<?> request, VolleyError error);
}

三個接口當中兩個是回應網絡應答的,最後一個回應網絡錯誤。追溯RequestQueue構造的時候,默認的分發者為ExecutorDelivery:

     public RequestQueue(Cache cache, Network network, int threadPoolSize) {
         this(cache, network, threadPoolSize,
                 new ExecutorDelivery(new Handler(Looper.getMainLooper())));
     }

可見,消息分發者工作在主線程上。

常見的分發者所做的工作有:

     @Override
     public void postResponse(Request<?> request, Response<?> response) { //發出響應
         postResponse(request, response, null);
     }

     @Override
     public void postResponse(Request<?> request, Response<?

> response, Runnable runnable) { //發出響應 request.markDelivered(); request.addMarker("post-response"); mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable)); } @Override public void postError(Request<?

> request, VolleyError error) { //發出錯誤響應 request.addMarker("post-error"); Response<?> response = Response.error(error); mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null)); }

這裏發現一個問題,事實上在NetworkDispatcher中的request.markDelivered()是多余的。在postResponse中已經運行了。不管是正常的響應還是錯誤都會運行ResponseDeliveryRunnable:

private class ResponseDeliveryRunnable implements Runnable {
         private final Request mRequest;
         private final Response mResponse;
         private final Runnable mRunnable;

         public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
             mRequest = request;
             mResponse = response;
             mRunnable = runnable; //若指定了runnable。如上面分析的在網絡請求有效可是須要更新的時候會指定一個runnable的
         }

         @SuppressWarnings("unchecked")
         @Override
         public void run() {
             // If this request has canceled, finish it and don‘t deliver.
             if (mRequest.isCanceled()) { //若請求被取消,結束並做標記
                 mRequest.finish("canceled-at-delivery");
                 return;
             }

             // Deliver a normal response or error, depending.
             if (mResponse.isSuccess()) { //若請求成功則處理回應
                 mRequest.deliverResponse(mResponse.result);
             } else {  //若不成功則處理錯誤
                 mRequest.deliverError(mResponse.error);
             }

             // If this is an intermediate response, add a marker, otherwise we‘re done
             // and the request can be finished.
             if (mResponse.intermediate) {
                 mRequest.addMarker("intermediate-response");
             } else {
                 mRequest.finish("done");
             }

             // If we have been provided a post-delivery runnable, run it.
             if (mRunnable != null) { //假設指定了額外的runnable這裏還會對它進行運行
                 mRunnable.run();
             }
        }
     }

Delivery作為網絡回應的分發、處理者,對回應數據進行了最後一層的把關。而當Delivery查詢回應是否成功時,由於Request已經對回應信息做過處理(檢查其成功還是錯誤)。所以能夠查詢到正確的狀態。若查詢到回應成功則會觸發Request的deliverResponse方法(以StringRequest為例):

     @Override
     protected void deliverResponse(String response) {
         mListener.onResponse(response);
     }
事實上就是觸發了用戶自己定義的網絡響應監聽器,mListener在StringRequest的構造中進行賦值:

     public StringRequest(int method, String url, Listener<String> listener,
             ErrorListener errorListener) {
         super(method, url, errorListener);
         mListener = listener;
     }

     public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
         this(Method.GET, url, listener, errorListener);
     }
當查詢到網絡回應數據不成功時候將觸發Request的deliverError方法,這種方法StringRequest並沒有復寫,所以追溯到其父類Request中:

     public void deliverError(VolleyError error) {
         if (mErrorListener != null) {
             mErrorListener.onErrorResponse(error);
         }
     }
這裏mErrorListener也是用戶在使用Volley時候自定的錯誤監聽器,在StringRequest中並沒有處理,是通過super運行Request的構造方法進行賦值的:

     public Request(int method, String url, Response.ErrorListener listener) {
         mMethod = method;
         mUrl = url;
         mErrorListener = listener;
         setRetryPolicy(new DefaultRetryPolicy());

         mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
     }
當這個請求已經完整的確定完畢後,Delivery會通知Request進行結束操作——finish:

     void finish(final String tag) {
         if (mRequestQueue != null) { //若請求隊列有效,則在請求隊列中標記當前請求為結束
             mRequestQueue.finish(this);
         }  //之後都是日誌相關,不做分析
         if (MarkerLog.ENABLED) {
             final long threadId = Thread.currentThread().getId();
             if (Looper.myLooper() != Looper.getMainLooper()) {
                 // If we finish marking off of the main thread, we need to
                 // actually do it on the main thread to ensure correct ordering.
                 Handler mainThread = new Handler(Looper.getMainLooper());
                 mainThread.post(new Runnable() {
                     @Override
                     public void run() {
                         mEventLog.add(tag, threadId);
                         mEventLog.finish(this.toString());
                     }
                 });
                 return;
             }

             mEventLog.add(tag, threadId);
             mEventLog.finish(this.toString());
         } else {
             long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
             if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
                 VolleyLog.d("%d ms: %s", requestTime, this.toString());
             }
         }
     }

mRequestQueue為RequestQueue類型,在開篇中就分析了RequestQueue,相關的另一個問題當時沒有進行挖掘,即mWaitingQueue中保留的同樣URL的多個請求層級何時才可以被觸發,下邊分析mRequestQueue的finish方法就能解開這個疑問了:

     void finish(Request<?> request) {
         // Remove from the set of requests currently being processed.
         synchronized (mCurrentRequests) {
             mCurrentRequests.remove(request); //當請求已完畢,會從mCurrentRequests隊列中被移除掉
         }

         if (request.shouldCache()) { //默認是true的,除非你調用Request的setShouldCache方法主動設定
             synchronized (mWaitingRequests) {
                 String cacheKey = request.getCacheKey(); //獲取cacheKey,前邊說過就是URL
                 Queue<Request<?

>> waitingRequests = mWaitingRequests.remove(cacheKey); //移除列表中的這個請求,同一時候取出其可能存在的層級關系 if (waitingRequests != null) { if (VolleyLog.DEBUG) { VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", waitingRequests.size(), cacheKey); } // Process all queued up requests. They won‘t be considered as in flight, but // that‘s not a problem as the cache has been primed by ‘request‘. mCacheQueue.addAll(waitingRequests); //若真的有層級關系,那麽將其它的請求所有增加到mCacheQueue中交由CacheDispatcher處理 } } } }

好了,終於待定的問題也攻克了。這就是一個Request網絡請求在Volley中的來龍去脈。


總結


1. 當一個RequestQueue被成功申請後會開啟一個CacheDispatcher(緩存調度器)和4個(默認)NetworkDispatcher(網絡請求調度器);

2. CacheDispatcher緩存調度器最為第一層緩沖,開始工作後堵塞的從緩存序列mCacheQueue中取得請求:

a. 對於已經取消了的請求。直接標記為跳過並結束這個請求

b. 全新或過期的請求。直接丟入mNetworkQueue中交由N個NetworkDispatcher進行處理

c. 已獲得緩存信息(網絡應答)卻沒有過期的請求,交由Request的parseNetworkResponse進行解析,從而確定此應答是否成功。然後將請求和應答交由Delivery分發者進行處理,假設須要更新緩存那麽該請求還會被放入mNetworkQueue中

3. 用戶將請求Request add到RequestQueue之後:

a. 對於不須要緩存的請求(須要額外設置,默認是須要緩存)直接丟入mNetworkQueue交由N個NetworkDispatcher處理。

b. 對於須要緩存的,全新的請求增加到mCacheQueue中給CacheDispatcher處理

c. 須要緩存,可是緩存列表中已經存在了同樣URL的請求,放在mWaitingQueue中做臨時雪藏,待之前的請求完成後。再又一次加入到mCacheQueue中;

4. 網絡請求調度器NetworkDispatcher作為網絡請求真實發生的地方。對消息交給BasicNetwork進行處理,相同的。請求和結果都交由Delivery分發者進行處理。

5. Delivery分發者實際上已經是對網絡請求處理的最後一層了,在Delivery對請求處理之前,Request已經對網絡應答進行過解析。此時應答成功與否已經設定。

而後Delivery依據請求所獲得的應答情況做不同處理:

a. 若應答成功,則觸發deliverResponse方法,終於會觸發開發人員為Request設定的Listener

b. 若應答失敗,則觸發deliverError方法,終於會觸發開發人員為Request設定的ErrorListener

處理完後,一個Request的生命周期就結束了,Delivery會調用Request的finish操作。將其從mRequestQueue中移除。與此同一時候,假設等待列表中存在同樣URL的請求,則會將剩余的層級請求所有丟入mCacheQueue交由CacheDispatcher進行處理。


一個Request的生命周期:

1. 通過add增加mRequestQueue中,等待請求被運行;

2. 請求運行後。調用自身的parseNetworkResponse對網絡應答進行處理。並推斷這個應答是否成功;

3. 若成功。則終於會觸發自身被開發人員設定的Listener。若失敗,終於會觸發自身被開發人員設定的ErrorListener。


至此Volley中網絡請求的來龍去脈分析清楚了,假設我們由於一些原因須要繼承Request來自己定義自己的Request,最須要註意的就是parseNetworkResponse方法的復寫。此方法對請求之後的命運有決定性的作用。

Android應用開發:網絡工具——Volley(二)