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

引言

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


源頭RequestQueue


在使用Volley前,必須有一個網絡請求隊列來承載請求,所以先分析一下這個請求隊列是如何申請,如果運作的。在Volley.java中:

  1. /** 
  2.   * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. 
  3.   * 
  4.   * @param context A {@link Context} to use for creating the cache dir. 
  5.   * @param stack An {@link HttpStack} to use for the network, or null for default. 
  6.   * @return A started {@link RequestQueue} instance. 
  7.   */  
  8.  public static RequestQueue newRequestQueue(Context context, HttpStack stack) {  
  9.      File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);  
  10.   
  11.      String userAgent = "volley/0";  
  12.      try {  
  13.          String packageName = context.getPackageName();  
  14.          PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);  
  15.          userAgent = packageName + "/" + info.versionCode;  
  16.      } catch (NameNotFoundException e) {  
  17.      }  
  18.   
  19.      if (stack == null) {  
  20.          if (Build.VERSION.SDK_INT >= 9) {  
  21.              stack = new HurlStack();  
  22.          } else {  
  23.              // Prior to Gingerbread, HttpUrlConnection was unreliable.  
  24.              // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html  
  25.              stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));  
  26.          }  
  27.      }  
  28.   
  29.      Network network = new BasicNetwork(stack);  
  30.   
  31.      RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);  
  32.      queue.start();  
  33.   
  34.      return queue;  
  35.  }  
  36.   
  37.  /** 
  38.   * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. 
  39.   * 
  40.   * @param context A {@link Context} to use for creating the cache dir. 
  41.   * @return A started {@link RequestQueue} instance. 
  42.   */  
  43.  public static RequestQueue newRequestQueue(Context context) {  
  44.      return newRequestQueue(context, null);  
  45.  }  

通常使用的是第二個接口,也就是隻有一個參數的newRequestQueue(Context context),使stack默認爲null。可以看到我們得到的RequestQueue是通過RequestQueue申請,然後又調用了其start方法,最後返回給我們的。接下來看一下RequestQueue的構造方法:

  1. /** 
  2.  * Creates the worker pool. Processing will not begin until {@link #start()} is called. 
  3.  * 
  4.  * @param cache A Cache to use for persisting responses to disk 
  5.  * @param network A Network interface for performing HTTP requests 
  6.  * @param threadPoolSize Number of network dispatcher threads to create 
  7.  * @param delivery A ResponseDelivery interface for posting responses and errors 
  8.  */  
  9. public RequestQueue(Cache cache, Network network, int threadPoolSize,  
  10.         ResponseDelivery delivery) {  
  11.     mCache = cache;  
  12.     mNetwork = network;  
  13.     mDispatchers = new NetworkDispatcher[threadPoolSize];  
  14.     mDelivery = delivery;  
  15. }  
  16.   
  17. /** 
  18.  * Creates the worker pool. Processing will not begin until {@link #start()} is called. 
  19.  * 
  20.  * @param cache A Cache to use for persisting responses to disk 
  21.  * @param network A Network interface for performing HTTP requests 
  22.  * @param threadPoolSize Number of network dispatcher threads to create 
  23.  */  
  24. public RequestQueue(Cache cache, Network network, int threadPoolSize) {  
  25.     this(cache, network, threadPoolSize,  
  26.             new ExecutorDelivery(new Handler(Looper.getMainLooper())));  
  27. }  
  28.   
  29. /** 
  30.  * Creates the worker pool. Processing will not begin until {@link #start()} is called. 
  31.  * 
  32.  * @param cache A Cache to use for persisting responses to disk 
  33.  * @param network A Network interface for performing HTTP requests 
  34.  */  
  35. public RequestQueue(Cache cache, Network network) {  
  36.     this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);  
  37. }  
RequestQueue有三種構造方法,通過newRequestQueue(Context context)調用的是最後一種。創建了一個工作池,默認承載網絡線程數量爲4個。而後兩種構造方法都會調用到第一個,進行了一些局部變量的賦值,並沒有什麼需要多說的,接下來看start()方法:

  1. public void start() {  
  2.     stop();  // Make sure any currently running dispatchers are stopped.  
  3.     // Create the cache dispatcher and start it.  
  4.     mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);  
  5.     mCacheDispatcher.start();  
  6.   
  7.     // Create network dispatchers (and corresponding threads) up to the pool size.  
  8.     for (int i = 0; i < mDispatchers.length; i++) {  
  9.         NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,  
  10.                 mCache, mDelivery);  
  11.         mDispatchers[i] = networkDispatcher;  
  12.         networkDispatcher.start();  
  13.     }  
  14. }  

首先進行了stop操作,將所有的執行者全部退出,從而確保當前沒有任何正在工作的執行者。然後主要的工作就是開啓一個CacheDispatcher和符合線程池數量的NetworkDispatcher。首先分析CacheDispatcher。


CacheDispatcher緩存操作


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

  1. @Override  
  2. public void run() {  
  3.     if (DEBUG) VolleyLog.v("start new dispatcher");  
  4.     Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  5.   
  6.     // Make a blocking call to initialize the cache.  
  7.     mCache.initialize(); //初始化一個緩存  
  8.   
  9.     while (true) {  
  10.         try {  
  11.             // Get a request from the cache triage queue, blocking until  
  12.             // at least one is available.  
  13.             final Request<?> request = mCacheQueue.take(); //在緩存序列中獲取請求,阻塞操作  
  14.             request.addMarker("cache-queue-take");  
  15.   
  16.             // If the request has been canceled, don't bother dispatching it.  
  17.             if (request.isCanceled()) { //若該請求已經被取消了,則直接跳過  
  18.                 request.finish("cache-discard-canceled");  
  19.                 continue;  
  20.             }  
  21.   
  22.             // Attempt to retrieve this item from cache.  
  23.             Cache.Entry entry = mCache.get(request.getCacheKey()); //嘗試在緩存中查找是否有緩存數據  
  24.             if (entry == null) {  
  25.                 request.addMarker("cache-miss"); //若沒有則緩存丟失,證明這個請求並沒有獲得實施過,扔進網絡請求隊列中  
  26.                 // Cache miss; send off to the network dispatcher.  
  27.                 mNetworkQueue.put(request);  
  28.                 continue;  
  29.             }  
  30.   
  31.             // If it is completely expired, just send it to the network.  
  32.             if (entry.isExpired()) { //若請求已經過期,那麼就要去獲取最新的消息,所以依然丟進網絡請求隊列中  
  33.                 request.addMarker("cache-hit-expired");  
  34.                 request.setCacheEntry(entry);  
  35.                 mNetworkQueue.put(request);  
  36.                 continue;  
  37.             }  
  38.   
  39.             // We have a cache hit; parse its data for delivery back to the request.  
  40.             request.addMarker("cache-hit");  
  41.             Response<?> response = request.parseNetworkResponse(  
  42.                     new NetworkResponse(entry.data, entry.responseHeaders)); //請求有緩存數據且沒有過期,那麼可以進行解析,交給請求的parseNetworkReponse方法進行解析,這個方法我們可以在自定義個Request中進行復寫自定義  
  43.             request.addMarker("cache-hit-parsed");  
  44.   
  45.             if (!entry.refreshNeeded()) { //如果請求有效且並不需要刷新,則丟進Delivery中處理,最終會觸發如StringRequest這樣的請求子類的onResponse或onErrorResponse  
  46.                 // Completely unexpired cache hit. Just deliver the response.  
  47.                 mDelivery.postResponse(request, response);  
  48.             } else { //請求有效,但是需要進行刷新,那麼需要丟進網絡請求隊列中  
  49.                 // Soft-expired cache hit. We can deliver the cached response,  
  50.                 // but we need to also send the request to the network for  
  51.                 // refreshing.  
  52.                 request.addMarker("cache-hit-refresh-needed");  
  53.                 request.setCacheEntry(entry);  
  54.   
  55.                 // Mark the response as intermediate.  
  56.                 response.intermediate = true;  
  57.   
  58.                 // Post the intermediate response back to the user and have  
  59.                 // the delivery then forward the request along to the network.  
  60.                 mDelivery.postResponse(request, response, new Runnable() {  
  61.                     @Override  
  62.                     public void run() {  
  63.                         try {  
  64.                             mNetworkQueue.put(request);  
  65.                         } catch (InterruptedException e) {  
  66.                             // Not much we can do about this.  
  67.                         }  
  68.                     }  
  69.                 });  
  70.             }  
  71.   
  72.         } catch (InterruptedException e) {  
  73.             // We may have been interrupted because it was time to quit.  
  74.             if (mQuit) {  
  75.                 return;  
  76.             }  
  77.             continue;  
  78.         }  
  79.     }  
  80. }  

CacheDispatcher做了很多事情,之後再來慢慢的消化他們。現在先看一下我們的請求通過add之後到了哪裏去。查看RequestQueue.java的add方法:

  1. /** 
  2.  * Adds a Request to the dispatch queue. 
  3.  * @param request The request to service 
  4.  * @return The passed-in request 
  5.  */  
  6. public <T> Request<T> add(Request<T> request) {  
  7.     // Tag the request as belonging to this queue and add it to the set of current requests.  
  8.     request.setRequestQueue(this);  
  9.     synchronized (mCurrentRequests) {  
  10.         mCurrentRequests.add(request); //加入到當前的隊列中,是一個HashSet  
  11.     }  
  12.   
  13.     // Process requests in the order they are added.  
  14.     request.setSequence(getSequenceNumber());  
  15.     request.addMarker("add-to-queue");  
  16.   
  17.     // If the request is uncacheable, skip the cache queue and go straight to the network.若這個請求不需要被緩存,需要直接做網絡請求,那麼就直接加到網絡請求隊列中  
  18.     if (!request.shouldCache()) {  
  19.         mNetworkQueue.add(request);  
  20.         return request;  
  21.     }  
  22.   
  23.     // Insert request into stage if there's already a request with the same cache key in flight.  
  24.     synchronized (mWaitingRequests) {  
  25.         String cacheKey = request.getCacheKey(); // Volley中使用請求的URL作爲存儲的key  
  26.         if (mWaitingRequests.containsKey(cacheKey)) { //若等待的請求中有與所請求的URL相同的請求,則需要做層級處理  
  27.             // There is already a request in flight. Queue up.  
  28.             Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);  
  29.             if (stagedRequests == null) {  
  30.                 stagedRequests = new LinkedList<Request<?>>();  
  31.             }  
  32.             stagedRequests.add(request);  
  33.             mWaitingRequests.put(cacheKey, stagedRequests); //若與已有的請求URL相同,則創建一個層級列表保存他們,然後再放入等待請求列表中  
  34.             if (VolleyLog.DEBUG) {  
  35.                 VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);  
  36.             }  
  37.         } else {  
  38.             // Insert 'null' queue for this cacheKey, indicating there is now a request in  
  39.             // flight.  
  40.             mWaitingRequests.put(cacheKey, null); //若是一個全新的請求,則直接放入等待隊列中,注意數據爲null,只有多個url產生層級關係了纔有數據  
  41.             mCacheQueue.add(request); //放入緩存隊列中,緩存隊列會對請求做處理  
  42.         }  
  43.         return request;  
  44.     }  
  45. }  

這裏的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個。

  1. public void start() {  
  2.     stop();  // Make sure any currently running dispatchers are stopped.  
  3.     // Create the cache dispatcher and start it.  
  4.     mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);  
  5.     mCacheDispatcher.start();  
  6.   
  7.     // Create network dispatchers (and corresponding threads) up to the pool size.  
  8.     for (int i = 0; i < mDispatchers.length; i++) {  
  9.         NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,  
  10.                 mCache, mDelivery);  
  11.         mDispatchers[i] = networkDispatcher;  
  12.         networkDispatcher.start();  
  13.     }  
  14. }  

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

  1. public NetworkDispatcher(BlockingQueue<Request<?>> queue,  
  2.         Network network, Cache cache,  
  3.         ResponseDelivery delivery) {  
  4.     mQueue = queue;  
  5.     mNetwork = network;  
  6.     mCache = cache;  
  7.     mDelivery = delivery;  
  8. }  
mQueue即爲mNetworkQueue,這與CacheDispatcher中使用到的是同一個。而mNetwork默認是BasicNetwork,mCache爲緩存,mDelivery爲最終的消息配發者,之後會分析到。接下來看其複寫的run()方法:

  1. @Override  
  2. public void run() {  
  3.     Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //設置線程可後臺運行,不會因爲系統休眠而掛起  
  4.     Request<?> request;  
  5.     while (true) {  
  6.         try {  
  7.             // Take a request from the queue.  
  8.             request = mQueue.take(); //mQueue即爲mNetworkQueue,從mNetworkQueue中獲取請求,也就是說CacheDispatcher丟過來的請求是從這裏被NetworkDispatcher獲取到的。注意這裏獲取請求是阻塞的。  
  9.         } catch (InterruptedException e) { //退出操作,NetworkDispatcher被設置成退出時候發出中斷請求  
  10.             // We may have been interrupted because it was time to quit.  
  11.             if (mQuit) {  
  12.                 return;  
  13.             }  
  14.             continue;  
  15.         }  
  16.   
  17.         try {  
  18.             request.addMarker("network-queue-take");  
  19.   
  20.             // If the request was cancelled already, do not perform the  
  21.             // network request.  
  22.             if (request.isCanceled()) { //若請求已經被取消,則標記爲完成(被取消),然後繼續下一個請求  
  23.                 request.finish("network-discard-cancelled");  
  24.                 continue;  
  25.             }  
  26.   
  27.             addTrafficStatsTag(request);  
  28.   
  29.             // Perform the network request.  
  30.             NetworkResponse networkResponse = mNetwork.performRequest(request); //使用BasicNetwork處理請求  
  31.             request.addMarker("network-http-complete");  
  32.   
  33.             // If the server returned 304 AND we delivered a response already,  
  34.             // we're done -- don't deliver a second identical response.  
  35.             if (networkResponse.notModified && request.hasHadResponseDelivered()) {  
  36.                 request.finish("not-modified");  
  37.                 continue;  
  38.             }  
  39.   
  40.             // Parse the response here on the worker thread.  
  41.             Response<?> response = request.parseNetworkResponse(networkResponse); //處理網絡請求應答數據  
  42.             request.addMarker("network-parse-complete");  
  43.   
  44.             // Write to cache if applicable.  
  45.             // TODO: Only update cache metadata instead of entire record for 304s.  
  46.             if (request.shouldCache() && response.cacheEntry != null) {  
  47.                 mCache.put(request.getCacheKey(), response.cacheEntry);  
  48.                 request.addMarker("network-cache-written");  
  49.             }  
  50.   
  51.             // Post the response back.  
  52.             request.markDelivered(); //標記請求爲已應答並做消息分發處理  
  53.             mDelivery.postResponse(request, response);  
  54.         } catch (VolleyError volleyError) {  
  55.             parseAndDeliverNetworkError(request, volleyError); //若產生Volley錯誤則會觸發Request的parseNetworkError方法以及mDelivery的postError方法  
  56.         } catch (Exception e) {  
  57.             VolleyLog.e(e, "Unhandled exception %s", e.toString());  
  58.             mDelivery.postError(request, new VolleyError(e)); //對於未知錯誤,只會觸發mDelivery的postError方法。  
  59.         }  
  60.     }  
  61. }  

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

  1. /** 
  2.   * Data and headers returned from {@link Network#performRequest(Request)}. 
  3.   */  
  4.  public class NetworkResponse {  
  5.      /** 
  6.       * Creates a new network response. 
  7.       * @param statusCode the HTTP status code 
  8.       * @param data Response body 
  9.       * @param headers Headers returned with this response, or null for none 
  10.       * @param notModified True if the server returned a 304 and the data was already in cache 
  11.       */  
  12.      public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,  
  13.              boolean notModified) {  
  14.          this.statusCode = statusCode;  
  15.          this.data = data;  
  16.          this.headers = headers;  
  17.          this.notModified = notModified;  
  18.      }  
  19.   
  20.      public NetworkResponse(byte[] data) {  
  21.          this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);  
  22.      }  
  23.   
  24.      public NetworkResponse(byte[] data, Map<String, String> headers) {  
  25.          this(HttpStatus.SC_OK, data, headers, false);  
  26.      }  
  27.   
  28.      /** The HTTP status code. */  
  29.      public final int statusCode;  
  30.   
  31.      /** Raw data from this response. */  
  32.      public final byte[] data;  
  33.   
  34.      /** Response headers. */  
  35.      public final Map<String, String> headers;  
  36.   
  37.      /** True if the server returned a 304 (Not Modified). */  
  38.      public final boolean notModified;  
  39.  }  
NetworkResponse保存了請求的迴應數據,包括數據本身和頭,還有狀態碼以及其他相關信息。根據請求類型的不同,對迴應數據的處理方式也各有不同,例如迴應是String和Json的區別。所以自然而然的網絡請求類型需要對它獲得的迴應數據自行處理,也就觸發了Request子類的parseNetworkResponse方法,下邊以StringRequest爲例進行分析:

  1. @Override  
  2. protected Response<String> parseNetworkResponse(NetworkResponse response) {  
  3.     String parsed;  
  4.     try {  
  5.         parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));  
  6.     } catch (UnsupportedEncodingException e) {  
  7.         parsed = new String(response.data);  
  8.     }  
  9.     return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));  
  10. }  
StringRequest中對於迴應首先嚐試解析數據和辨別頭數據編碼類型,若失敗則只解析數據部分。最終都是觸發Request的success方法,參數中還使用Volley自帶的HttpHeaderParser對頭信息進行了解析。需要看一下Response的success方法究竟做了什麼,鑑於Response類總共沒有多少代碼,就全部拿出來做分析了:

  1. public class Response<T> {  
  2.   
  3.     /** 處理解析過的迴應信息的回調接口 */  
  4.     public interface Listener<T> {  
  5.         /** 當接收到迴應後 */  
  6.         public void onResponse(T response);  
  7.     }  
  8.   
  9.     /** 處理錯誤迴應的回調接口 */  
  10.     public interface ErrorListener {  
  11.         /** 
  12.          * 錯誤發生時的回調接口 
  13.          */  
  14.         public void onErrorResponse(VolleyError error);  
  15.     }  
  16.   
  17.     /** 返回一個包含已解析結果的成功迴應 */  
  18.     public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {  
  19.         return new Response<T>(result, cacheEntry);  
  20.     }  
  21.   
  22.     /** 
  23.      * 返回錯誤迴應,包含錯誤碼以及可能的其他消息 
  24.      */  
  25.     public static <T> Response<T> error(VolleyError error) {  
  26.         return new Response<T>(error);  
  27.     }  
  28.   
  29.     /** 解析過的響應信息,錯誤時爲null */  
  30.     public final T result;  
  31.   
  32.     /** 響應的緩存數據,錯誤時爲null */  
  33.     public final Cache.Entry cacheEntry;  
  34.   
  35.     /** 詳細的錯誤信息 */  
  36.     public final VolleyError error;  
  37.   
  38.     /** 此迴應軟件希望得到第二次迴應則爲true,即需要刷新 */  
  39.     public boolean intermediate = false;  
  40.   
  41.     /** 
  42.      * 返回true代表迴應成功,沒有錯誤。有錯誤則爲false 
  43.      */  
  44.     public boolean isSuccess() {  
  45.         return error == null;  
  46.     }  
  47.   
  48.   
  49.     private Response(T result, Cache.Entry cacheEntry) {  
  50.         this.result = result;  
  51.         this.cacheEntry = cacheEntry;  
  52.         this.error = null;  
  53.     }  
  54.   
  55.     private Response(VolleyError error) {  
  56.         this.result = null;  
  57.         this.cacheEntry = null;  
  58.         this.error = error;  
  59.     }  
  60. }  
這就是網絡響應的類,很簡單,成功或錯誤都會直接進行標記,通過isSuccess接口提供外部查詢。如果響應成功,則消息保存在result中,解析頭信息得到的緩存數據保存在cacheEntry中。

Request作爲基類,Volley自帶的又代表性的其擴展類又StringRequest和JsonObjectRequest,如果開發者有比較大的自定義需求就需要繼承Request複寫內部一些重要的方法。同時mDelivery出場的機會這麼多,爲什麼他總出現在處理請求的地方呢?下邊就對它和Request一起進行分析,其中Request依然以StringRequest爲例。

ExecutorDelivery消息分發者與Request請求

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

  1. public interface ResponseDelivery {  
  2.     /** 
  3.      * Parses a response from the network or cache and delivers it. 
  4.      */  
  5.     public void postResponse(Request<?> request, Response<?> response);  
  6.   
  7.     /** 
  8.      * Parses a response from the network or cache and delivers it. The provided 
  9.      * Runnable will be executed after delivery. 
  10.      */  
  11.     public void postResponse(Request<?> request, Response<?> response, Runnable runnable);  
  12.   
  13.     /** 
  14.      * Posts an error for the given request. 
  15.      */  
  16.     public void postError(Request<?> request, VolleyError error);  
  17. }  

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

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

可見,消息分發者工作在主線程上。常見的分發者所做的工作有:

  1. @Override  
  2. public void postResponse(Request<?> request, Response<?> response) { //發出響應  
  3.     postResponse(request, response, null);  
  4. }  
  5.   
  6. @Override  
  7. public void postResponse(Request<?> request, Response<?> response, Runnable runnable) { //發出響應  
  8.     request.markDelivered();  
  9.     request.addMarker("post-response");  
  10.     mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));  
  11. }  
  12.   
  13. @Override  
  14. public void postError(Request<?> request, VolleyError error) { //發出錯誤響應  
  15.     request.addMarker("post-error");  
  16.     Response<?> response = Response.error(error);  
  17.     mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));  
  18. }  
這裏發現一個問題,其實在NetworkDispatcher中的request.markDelivered()是多餘的,在postResponse中已經執行了。無論是正常的響應還是錯誤都會執行ResponseDeliveryRunnable:

  1. private class ResponseDeliveryRunnable implements Runnable {  
  2.          private final Request mRequest;  
  3.          private final Response mResponse;  
  4.          private final Runnable mRunnable;  
  5.   
  6.          public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {  
  7.              mRequest = request;  
  8.              mResponse = response;  
  9.              mRunnable = runnable; //若指定了runnable,如上面分析的在網絡請求有效但是需要更新的時候會指定一個runnable的  
  10.          }  
  11.   
  12.          @SuppressWarnings("unchecked")  
  13.          @Override  
  14.          public void run() {  
  15.              // If this request has canceled, finish it and don't deliver.  
  16.              if (mRequest.isCanceled()) { //若請求被取消,結束並做標記  
  17.                  mRequest.finish("canceled-at-delivery");  
  18.                  return;  
  19.              }  
  20.   
  21.              // Deliver a normal response or error, depending.  
  22.              if (mResponse.isSuccess()) { //若請求成功則處理迴應  
  23.                  mRequest.deliverResponse(mResponse.result);  
  24.              } else {  //若不成功則處理錯誤  
  25.                  mRequest.deliverError(mResponse.error);  
  26.              }  
  27.   
  28.              // If this is an intermediate response, add a marker, otherwise we're done  
  29.              // and the request can be finished.  
  30.              if (mResponse.intermediate) {  
  31.                  mRequest.addMarker("intermediate-response");  
  32.              } else {  
  33.                  mRequest.finish("done");  
  34.              }  
  35.   
  36.              // If we have been provided a post-delivery runnable, run it.  
  37.              if (mRunnable != null) { //如果指定了額外的runnable這裏還會對它進行執行  
  38.                  mRunnable.run();  
  39.              }  
  40.         }  
  41.      }  

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

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

  1. public StringRequest(int method, String url, Listener<String> listener,  
  2.         ErrorListener errorListener) {  
  3.     super(method, url, errorListener);  
  4.     mListener = listener;  
  5. }  
  6.   
  7. public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {  
  8.     this(Method.GET, url, listener, errorListener);  
  9. }  
當查詢到網絡迴應數據不成功時候將觸發Request的deliverError方法,這個方法StringRequest並沒有複寫,所以追溯到其父類Request中:

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

  1. public Request(int method, String url, Response.ErrorListener listener) {  
  2.     mMethod = method;  
  3.     mUrl = url;  
  4.     mErrorListener = listener;  
  5.     setRetryPolicy(new DefaultRetryPolicy());  
  6.   
  7.     mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);  
  8. }  
當這個請求已經完整的確定完成後,Delivery會通知Request進行結束操作——finish:

  1. void finish(final String tag) {  
  2.     if (mRequestQueue != null) { //若請求隊列有效,則在請求隊列中標記當前請求爲結束  
  3.         mRequestQueue.finish(this);  
  4.     }  //之後都是日誌相關,不做分析  
  5.     if (MarkerLog.ENABLED) {  
  6.         final long threadId = Thread.currentThread().getId();  
  7.         if (Looper.myLooper() != Looper.getMainLooper()) {  
  8.             // If we finish marking off of the main thread, we need to  
  9.             // actually do it on the main thread to ensure correct ordering.  
  10.             Handler mainThread = new Handler(Looper.getMainLooper());  
  11.             mainThread.post(new Runnable() {  
  12.                 @Override  
  13.                 public void run() {  
  14.                     mEventLog.add(tag, threadId);  
  15.                     mEventLog.finish(this.toString());  
  16.                 }  
  17.             });  
  18.             return;  
  19.         }  
  20.   
  21.         mEventLog.add(tag, threadId);  
  22.         mEventLog.finish(this.toString());  
  23.     } else {  
  24.         long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;  
  25.         if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {  
  26.             VolleyLog.d("%d ms: %s", requestTime, this.toString());  
  27.         }  
  28.     }  
  29. }  

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

  1. void finish(Request<?> request) {  
  2.     // Remove from the set of requests currently being processed.  
  3.     synchronized (mCurrentRequests) {  
  4.         mCurrentRequests.remove(request); //當請求已完成,會從mCurrentRequests隊列中被移除掉  
  5.     }  
  6.   
  7.     if (request.shouldCache()) { //默認是true的,除非你調用Request的setShouldCache方法主動設定  
  8.         synchronized (mWaitingRequests) {  
  9.             String cacheKey = request.getCacheKey(); //獲取cacheKey,前邊說過就是URL  
  10.             Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey); //移除列表中的這個請求,同時取出其可能存在的層級關係  
  11.             if (waitingRequests != null) {  
  12.                 if (VolleyLog.DEBUG) {  
  13.                     VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",  
  14.                             waitingRequests.size(), cacheKey);  
  15.                 }  
  16.                 // Process all queued up requests. They won't be considered as in flight, but  
  17.                 // that's not a problem as the cache has been primed by 'request'.  
  18.                 mCacheQueue.addAll(waitingRequests); //若真的有層級關係,那麼將其他的請求全部加入到mCacheQueue中交由CacheDispatcher處理  
  19.             }  
  20.         }  
  21.     }  
  22. }  
好了,最終待定的問題也解決了,這就是一個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方法的複寫,此方法對請求之後的命運有決定性的作用。

發佈了48 篇原創文章 · 獲贊 3 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章