從簡單的StringRequest入手看看Volley的工做機制。html
先簡單說下Volley的用法:android
① 獲取一個RequestQueueapi
mRequestQueue = Volley.newRequestQueue(this);
② 構造一個StringRequest對象緩存
mStringRequest = new StringRequest(url, new Response.Listener<String>() { @Override public void onResponse(String response) { mTextView.setText(response); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.d(TAG, "onErrorResponse: " + error.getMessage()); } });
③ 將StringRequest對象add進RequestQueue網絡
mRequestQueue.add(mStringRequest);
下面經過源碼跟蹤一下Volley處理請求的過程:app
用過Volley都知道,請求通常是繼承自Request這個抽象類,那麼StringRequest天然也是。在構造方法中須要幾個參數,具體的構造方法以下框架
public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) { super(method, url, errorListener); mListener = listener; }
能夠看到咱們須要給它指定請求的方法、url、成功返回的回調類和錯誤的回調類。socket
接着咱們在須要請求的時候,把這個對象傳遞給RequestQueue的add方法。這個add方法是作什麼的咱們其實都能猜到,就是把這個請求放到隊列中。ide
1 public <T> Request<T> add(Request<T> request) { 2 // Tag the request as belonging to this queue and add it to the set of current requests. 3 request.setRequestQueue(this); 4 synchronized (mCurrentRequests) { 5 mCurrentRequests.add(request); 6 } 7 8 // Process requests in the order they are added. 9 request.setSequence(getSequenceNumber()); 10 request.addMarker("add-to-queue"); 11 12 // If the request is uncacheable, skip the cache queue and go straight to the network. 13 if (!request.shouldCache()) { 14 mNetworkQueue.add(request); 15 return request; 16 } 17 18 // Insert request into stage if there's already a request with the same cache key in flight. 19 synchronized (mWaitingRequests) { 20 String cacheKey = request.getCacheKey(); 21 if (mWaitingRequests.containsKey(cacheKey)) { 22 // There is already a request in flight. Queue up. 23 Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey); 24 if (stagedRequests == null) { 25 stagedRequests = new LinkedList<Request<?>>(); 26 } 27 stagedRequests.add(request); 28 mWaitingRequests.put(cacheKey, stagedRequests); 29 if (VolleyLog.DEBUG) { 30 VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); 31 } 32 } else { 33 // Insert 'null' queue for this cacheKey, indicating there is now a request in 34 // flight. 35 mWaitingRequests.put(cacheKey, null); 36 mCacheQueue.add(request); 37 } 38 return request; 39 } 40 }
代碼可能有點長,可是核心的地方也比較簡單,就是進行線程同步以後將請求放入請求的集合mNetworkQueue(在不須要緩存的狀況下)。若是須要緩存請求,則同步等待序列,判斷請求是否已經被髮出過,根據狀況返回請求。oop
由於這裏會根據是否須要緩存進行區別處理,下面按照不須要緩存來說解源碼。而StringRequest是會進行緩存的。
接着咱們看看RequestQueue是怎麼工做的。這裏其實咱們也是能夠知道,既然命名爲Queue,確定是會經過一個線程來不停的遍歷隊列中的等待者而後進行處理,跟Handler中的MessageQueue是很相似的。
咱們先看RequestQueue的構造,咱們通常經過下面這個方法來得到一個RequestQueue
mRequestQueue = Volley.newRequestQueue(this);
而實際上這個構造的方法內容是下面這樣的
1 public static RequestQueue newRequestQueue(Context context, HttpStack stack) { 2 File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); 3 4 String userAgent = "volley/0"; 5 try { 6 String packageName = context.getPackageName(); 7 PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); 8 userAgent = packageName + "/" + info.versionCode; 9 } catch (NameNotFoundException e) { 10 } 11 12 if (stack == null) { 13 if (Build.VERSION.SDK_INT >= 9) { 14 stack = new HurlStack(); 15 } else { 16 // Prior to Gingerbread, HttpUrlConnection was unreliable. 17 // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html 18 stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); 19 } 20 } 21 22 Network network = new BasicNetwork(stack); 23 24 RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); 25 queue.start(); 26 27 return queue; 28 }
從註釋能夠看到,在api等級大於9的時候,使用HttpUrlConnection來進行主要的網絡請求工做,到這裏已經很明顯了,Volley底層是使用HttpUrlConnection進行的。咱們看到這裏是使用了一個HttpClientStack來包裝根據userAgent獲得的HttpClient(這個類在最新的源碼中已經被移除了),而HttpClient實際上就是使用HttpUrlConnection來實現的。最後被包裝爲一個BasicNetwork對象。
接着根據獲得的BasicNetwork對象和一個DiskBasedCache對象(磁盤緩存)來構造一個RequestQueue,而且調用了它的start方法來啓動這個線程。
再看看RequestQueue的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 }
這裏咱們先不顧這個mCacheDispatcher,直接看到下面的for循環,這個for循環遍歷了mDispatchers,這個mDispatcher其實至關於一個線程池,這個線程池的大小默認是4。而後分別讓這裏面的線程運行起來(調用了它們的start方法)。這裏爲何要有多個線程來處理呢?緣由很簡單,由於咱們每個請求都不必定會立刻處理完畢,多個線程進行同時處理的話效率會提升。
咱們進入NetworkDispatcher看看它的run方法:
1 @Override 2 public void run() { 3 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 4 while (true) { 5 long startTimeMs = SystemClock.elapsedRealtime(); 6 Request<?> request; 7 try { 8 // Take a request from the queue. 9 request = mQueue.take(); 10 } catch (InterruptedException e) { 11 // We may have been interrupted because it was time to quit. 12 if (mQuit) { 13 return; 14 } 15 continue; 16 } 17 18 try { 19 request.addMarker("network-queue-take"); 20 21 // If the request was cancelled already, do not perform the 22 // network request. 23 if (request.isCanceled()) { 24 request.finish("network-discard-cancelled"); 25 continue; 26 } 27 28 addTrafficStatsTag(request); 29 30 // Perform the network request. 31 NetworkResponse networkResponse = mNetwork.performRequest(request); 32 request.addMarker("network-http-complete"); 33 34 // If the server returned 304 AND we delivered a response already, 35 // we're done -- don't deliver a second identical response. 36 if (networkResponse.notModified && request.hasHadResponseDelivered()) { 37 request.finish("not-modified"); 38 continue; 39 } 40 41 // Parse the response here on the worker thread. 42 Response<?> response = request.parseNetworkResponse(networkResponse); 43 request.addMarker("network-parse-complete"); 44 45 // Write to cache if applicable. 46 // TODO: Only update cache metadata instead of entire record for 304s. 47 if (request.shouldCache() && response.cacheEntry != null) { 48 mCache.put(request.getCacheKey(), response.cacheEntry); 49 request.addMarker("network-cache-written"); 50 } 51 52 // Post the response back. 53 request.markDelivered(); 54 mDelivery.postResponse(request, response); 55 } catch (VolleyError volleyError) { 56 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); 57 parseAndDeliverNetworkError(request, volleyError); 58 } catch (Exception e) { 59 VolleyLog.e(e, "Unhandled exception %s", e.toString()); 60 VolleyError volleyError = new VolleyError(e); 61 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); 62 mDelivery.postError(request, volleyError); 63 } 64 } 65 }
第三行設置了這些線程的優先級,這個優先級比較低,目的是爲了儘可能減小對UI線程的影響保證流暢度。
接着第9行,調用mQueue的take方法取出隊列頭的一個請求進行處理,這個mQueue是什麼?其實它就是咱們在上面add方法中添加進去的一個請求。
咱們不看這些設置狀態標記的地方,直接看到第31行,若是請求沒有被取消,也就是正常的狀況下,咱們會調用mNetwork的performRequest方法進行請求的處理。不知道你還記的這個mNetwork不,它其實就是咱們上面提到的那個由HttpUrlConnection層層包裝的網絡請求對象。
若是請求獲得告終果,咱們會看到54行調用了mDelivery的postResponose方法來回傳咱們的請求結果。
這裏還有兩個重要的地方須要再瞭解一下,一個是究竟postResponse是怎麼傳回咱們的請求結果的,另外一個就是performRequest是怎麼去進行網絡請求的。
先看第一個,結果的回傳。咱們先了解下這個mDelivery是怎麼定義的。它實際上是在RequestQueue中建立的,能夠看到RequestQueue的其中一個構造方法:
1 public RequestQueue(Cache cache, Network network, int threadPoolSize) { 2 this(cache, network, threadPoolSize, 3 new ExecutorDelivery(new Handler(Looper.getMainLooper()))); 4 }
這裏直接就new了一個ExecutorDelivery對象,並傳入了一個不斷從MainLooper中獲取Message的Handler。再看看postResponse方法的內容:
1 @Override 2 public void postResponse(Request<?> request, Response<?> response, Runnable runnable) { 3 request.markDelivered(); 4 request.addMarker("post-response"); 5 mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable)); 6 }
這裏看到第5行調用了mResponsePoster的execute方法並傳入了一個ResponseDeliveryRunnable對象,再看mResponsePoster的定義:
1 public ExecutorDelivery(final Handler handler) { 2 // Make an Executor that just wraps the handler. 3 mResponsePoster = new Executor() { 4 @Override 5 public void execute(Runnable command) { 6 handler.post(command); 7 } 8 }; 9 }
也就是咱們在這裏把ResponseDeliveryRunnable對象經過Handler的post方法發送出去了。這裏爲何要發送到MainLooper中?由於RequestQueue是在子線程中執行的,回調到的代碼也是在子線程中的,若是在回調中修改UI,就會報錯。再者,爲何要使用post方法?緣由也很簡單,由於咱們在消息發出以後再進行回調,post方法容許咱們傳入一個Runnable的實現了,post成功會自動執行它的run方法,這個時候在run方法中進行結果的判斷而且進行回調:
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; 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) { 38 mRunnable.run(); 39 } 40 } 41 }
能夠看到,23行是調用Request的deleverResponse方法將結果回調給StringRequest。接着看看StringRequest中該方法是實現:
1 @Override 2 protected void deliverResponse(String response) { 3 mListener.onResponse(response); 4 }
直接經過咱們構造StringRequest時傳進來的Listener的回調方法onResponse來將結果回調給Activity。deleverError也是一樣的作法。
再來看看performRequest是怎麼進行網絡請求的。
mNetwork是Network接口的對象,而這個接口只有一個實現類,就是BasicNetwork,咱們看看這個BasicNetwork中的performRequest的代碼:
1 @Override 2 public NetworkResponse performRequest(Request<?> request) throws VolleyError { 3 long requestStart = SystemClock.elapsedRealtime(); 4 while (true) { 5 HttpResponse httpResponse = null; 6 byte[] responseContents = null; 7 Map<String, String> responseHeaders = Collections.emptyMap(); 8 try { 9 // Gather headers. 10 Map<String, String> headers = new HashMap<String, String>(); 11 addCacheHeaders(headers, request.getCacheEntry()); 12 httpResponse = mHttpStack.performRequest(request, headers); 13 StatusLine statusLine = httpResponse.getStatusLine(); 14 int statusCode = statusLine.getStatusCode(); 15 16 responseHeaders = convertHeaders(httpResponse.getAllHeaders()); 17 // Handle cache validation. 18 if (statusCode == HttpStatus.SC_NOT_MODIFIED) { 19 20 Entry entry = request.getCacheEntry(); 21 if (entry == null) { 22 return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null, 23 responseHeaders, true, 24 SystemClock.elapsedRealtime() - requestStart); 25 } 26 27 // A HTTP 304 response does not have all header fields. We 28 // have to use the header fields from the cache entry plus 29 // the new ones from the response. 30 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 31 entry.responseHeaders.putAll(responseHeaders); 32 return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data, 33 entry.responseHeaders, true, 34 SystemClock.elapsedRealtime() - requestStart); 35 } 36 37 // Some responses such as 204s do not have content. We must check. 38 if (httpResponse.getEntity() != null) { 39 responseContents = entityToBytes(httpResponse.getEntity()); 40 } else { 41 // Add 0 byte response as a way of honestly representing a 42 // no-content request. 43 responseContents = new byte[0]; 44 } 45 46 // if the request is slow, log it. 47 long requestLifetime = SystemClock.elapsedRealtime() - requestStart; 48 logSlowRequests(requestLifetime, request, responseContents, statusLine); 49 50 if (statusCode < 200 || statusCode > 299) { 51 throw new IOException(); 52 } 53 return new NetworkResponse(statusCode, responseContents, responseHeaders, false, 54 SystemClock.elapsedRealtime() - requestStart); 55 } catch (SocketTimeoutException e) { 56 attemptRetryOnException("socket", request, new TimeoutError()); 57 } catch (ConnectTimeoutException e) { 58 attemptRetryOnException("connection", request, new TimeoutError()); 59 } catch (MalformedURLException e) { 60 throw new RuntimeException("Bad URL " + request.getUrl(), e); 61 } catch (IOException e) { 62 int statusCode = 0; 63 NetworkResponse networkResponse = null; 64 if (httpResponse != null) { 65 statusCode = httpResponse.getStatusLine().getStatusCode(); 66 } else { 67 throw new NoConnectionError(e); 68 } 69 VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); 70 if (responseContents != null) { 71 networkResponse = new NetworkResponse(statusCode, responseContents, 72 responseHeaders, false, SystemClock.elapsedRealtime() - requestStart); 73 if (statusCode == HttpStatus.SC_UNAUTHORIZED || 74 statusCode == HttpStatus.SC_FORBIDDEN) { 75 attemptRetryOnException("auth", 76 request, new AuthFailureError(networkResponse)); 77 } else { 78 // TODO: Only throw ServerError for 5xx status codes. 79 throw new ServerError(networkResponse); 80 } 81 } else { 82 throw new NetworkError(networkResponse); 83 } 84 } 85 }
這段代碼中,先10和11行代碼將cache的屬性設置給header,接着第9行調用mHttpStack對象的performRequest方法並傳入請求對象和頭部來進行請求,獲得一個HttpResponse對象。
接着將HttpResponse對象中的狀態嗎取出,若是值爲HttpStatus.SC_NOT_MODIFIED(也就是304),則表示請求獲得的Response沒有變化,直接顯示緩存內容。
第39行表示請求成功而且獲取到請求內容,將內容取出並做爲一個NetworkResponse對象的屬性並返回給NetworkDispatcher,接着將其轉,接着就是上面介紹的回調給主線程了。
用過HttpClient的都知道,其實這裏獲得的HttpResponse就是由HttpClient返回的,咱們直接看第12行調用的performRequest的源碼:
1 @Override 2 public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) 3 throws IOException, AuthFailureError { 4 HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders); 5 addHeaders(httpRequest, additionalHeaders); 6 addHeaders(httpRequest, request.getHeaders()); 7 onPrepareRequest(httpRequest); 8 HttpParams httpParams = httpRequest.getParams(); 9 int timeoutMs = request.getTimeoutMs(); 10 // TODO: Reevaluate this connection timeout based on more wide-scale 11 // data collection and possibly different for wifi vs. 3G. 12 HttpConnectionParams.setConnectionTimeout(httpParams, 5000); 13 HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); 14 return mClient.execute(httpRequest); 15 }
這裏14行的mClient其實就是一個HttpClient對象。
說到這裏,是否是感受很混亂呢?這裏我作了一個圖,能夠參考看看
咱們這裏簡單再解析下:
① 在RequestQueue建立的時候,會生成多個NetwrokDispatcher,接着這些NetwrokDispatcher會不斷的從請求隊列中讀取請求,若是有就使用包裝好的請求類來執行performRequest,接着將結果經過postResponse方法傳包裝好並經過post方法發送到MainLooper。
② 在MainLooper中,判斷Response是否有內容,經過deliverResponse將結果回調給RequestQueue,RequestQueue經過咱們構造時傳入的Listener中的回調方法對結果進行回調。
③ 在咱們須要請求的時候,構建一個StringRequest(設置好對應的回調接口和實現回調方法)並將其add到MessageQueue中便可自動完成請求。
咱們應該掌握什麼?
① 子線程中應該如何將結果回調給主線程
② 若是本身要設計一個相似的框架,知道如何進行設計保證低耦合和便於維護
③ 經過學習其餘的Request子類定義咱們本身的請求類
④ 閱讀源代碼的技巧,好比查看直接子類,方法和參數定義查看等等