Volley 是谷歌推出的一款輕便的網絡請求庫,所有不足 50 個文件,與 OkHttp 相比實在算不上臃腫。使用上也比較方便,你能夠自由地定義本身的 Request 類,直接將返回數據轉換成本身須要的格式,若是你須要使用 ImageView 展現網絡圖片的話那就更巧了,Volley 提供了一個 NetworkImageView 類,只須要像設置本地圖片文件同樣將網絡圖片的 url 設置進去,它就會自動加載圖片並展現。apache
輕便並不意味着功能的不豐富,從自定義 Request 到緩存,再到返回數據的分發,Volley 都提供了可供用戶自定義功能的接口,只要你須要的功能沒有那麼苛刻,Volley 基本都能知足平常的網絡訪問須要。json
同時,輕便意味着負載小,從發起網絡請求,到收到返回數據,在 Volley 中走過的流程並很少,而且它內部還有一個輕量級的線程池,可以更有效地執行請求。緩存
下面先從一個標準的網絡請求過一遍 Volley 的代碼。安全
使用 Volley 以前要先建立一個 RequestQueue 用於分發執行 Request ,建立 RequestQueue 實例有兩種方法,一種是使用工廠類 Volley ,Volley 提供了四個方法用於建立 RequestQueue 實例,分別是:bash
publicstaticRequestQueuenewRequestQueue(Contextcontext);
publicstaticRequestQueuenewRequestQueue(Contextcontext, BaseHttpStackstack);
publicstaticRequestQueuenewRequestQueue(Contextcontext, HttpStackstack);複製代碼
第2、三個方法的第二個參數分別是 BaseHttpStack 和 HttpStack ,它們的做用是執行具體的網絡請求,用戶能夠繼承/實現它們使用本身的網絡請求功能,如能夠內置使用 Okhttp 執行實際的網絡請求,不過須要將 Okhttp 返回的 Resopnse 轉換成 Volley 的 HttpResponse 。markdown
兩個類分別要實現的方法是:網絡
// BaseHttpStack
publicabstractHttpResponseexecuteRequest(
Request<?>request, Map<String, String>additionalHeaders)
throwsIOException, AuthFailureError;
// HttpStack
org.apache.http.HttpResponseperformRequest(Request<?>request, Map<String, String>additionalHeaders)
throwsIOException, AuthFailureError;複製代碼
目前 HttpStack 已經被標誌爲 Deprecated,緣由是它使用的仍是 org.apache.http.HttpResponse ,而最新的 BaseHttpStack 使用的則是 Volley 本身實現的 HttpResponse ,這裏爲了實現兼容,Volley 專門提供了兩個類 BaseHttpStack 和 AdapterHttpStack 用於兩者之間的轉換,這裏先按下不表。多線程
再說 RequestQueue 實例的建立,除了使用 Volley 以外,用戶也能夠直接調用 RequestQueue 的構造方法,構造方法有三個重載,分別是:app
publicRequestQueue(
Cachecache, Networknetwork, intthreadPoolSize, ResponseDeliverydelivery);
publicRequestQueue(Cachecache, Networknetwork, intthreadPoolSize);
publicRequestQueue(Cachecache, Networknetwork);複製代碼
NetWork ,它聲明瞭一個方法NetworkResponse performRequest(Request<?> request) throws VolleyError;
,用於從 Request 獲得 NetworkResponse ,即網絡請求的實現,Volley 有一個默認的實現 BasicNetwork ,即在使用 Volley 類建立 RequestQueue 時使用的類,但它也並非真正的網絡請求類,上面也說到真正的執行網絡請求的是 HttpStack 和 BaseHttpStack,Volley 也有一個默認的實現,繼承自 BaseHttpStack 的 HurlStack 和實現自 HttpStack 的 HttpClientStack 。ide
四個參數的意義都很明確,Cache 中聲明瞭一些緩存相關的方法,在 Volley 進行網絡請求的時候它會將本身認爲須要緩存的請求使用 Cache 緩存,Cache 能夠由用戶本身實現,也可使用 Volley 提供了一個默認 Cache ,DiskBaseCache ,該緩存會將數據保存在文件中,若是你以爲保存在文件中比較慢,也能夠利用 LruCache 實現一個內存緩存類。
Cache,緩存類
Network,網絡請求類
threadPoolSize,線程池大小
ResponseDelivery,Response 分發類
後者都是在增長了一個默認參數的狀況下調用前者,因此只看第一個構造方法,它有四個參數:
threadPoolSize 就是線程池中存在的線程數,RequestQueue 以後會根據這個參數建立線程池。
ResponseDelivery ,用於分發 Response ,從 Volley 的默認實現來看,它能夠用做線程切換,並對 Request 和 Response 作一些額外的處理,如將其加入緩存等。 建立實例以後,還須要再調用 RequestQueue 的start()
方法完成啓動,方法實現以下:
/** Starts the dispatchers in this queue. */ publicvoidstart() { stop(); // Make sure any currently running dispatchers are stopped. // Create the cache dispatcher and start it. mCacheDispatcher=newCacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start(); // Create network dispatchers (and corresponding threads) up to the pool size. for(inti=0; i<mDispatchers.length; i++) { NetworkDispatchernetworkDispatcher= newNetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] =networkDispatcher; networkDispatcher.start(); } }複製代碼
這個方法建立了一個 CacheDispatcher 和 n 個 NetworkDispacher ,這裏的 n 就是構造方法中傳入的 threadPoolSize 。每個 Dispatcher 都是一個小型的 Looper ,它們從 RequestQueue 中取 Request 並執行請求。用戶調用add()
方法將一個 Request 放入 RequestQueue 中。在add()
方法中,RequestQueue 先判斷 Request 是否能夠緩存,而後將其加入到 mCacheQueue 或是 mNetworkQueue ,這兩種不一樣的隊列正是對應着start()
方法中初始化的兩種 Dispatcher 。
Volley 中共有兩種 Dispatcher ,其中 NetwrokDispatcher 分發的是須要直接執行的請求,CacheDispatcher 分發的是可緩存的請求,換句話說,請求以前也須要檢查是否已經存在緩存,緩存是否可用等。
它們有着共同的邏輯,都是繼承於 Thread ,啓動以後便不斷調用processRequest()
方法從隊列中取出 Request ,而後調用processRequest(Request)
處理 Request ,兩者也就區別於此。
在 CacheDispatcher 中,processRequest(Request)
會先根據 Request key 從 Cache 中查找有沒有緩存,若是沒有,則將其加入到 NetwordDispatcher 中等待處理,不過在加入 NetworkDispatcher 以前還須要通過一步判斷,調用maybeAddToWaitingRequests(Request)
方法判斷這個方法是否須要交給 NetworkDispatcher 處理。由於在某些狀況下,CacheDispatcher 可能會短期內同時處理兩個相同的請求,可是這兩個請求都沒有緩存,因此要交給 NetworkDispatcher 處理,可是又沒必要都交給 NetworkDispatcher 處理,由於兩個請求相同,第二個請求只須要複用第一個請求的結果便可,這個判斷過程就是maybeAddToWaitingRequests(Request)
要執行的,它會將後續的請求放到暫存到一個列表中,並監聽第一個 Request 的請求狀況,當請求完成的時候,就會再將結果分發給這些相同的 Request。
總結來講,就是 CacheDispatcher 會優先從查找緩存,若是沒有,就會將請求交給 NetworkDispatcher ,而且會截斷重複請求。
NetworkDispatcher 與 CacheDispatcher 有着相似的邏輯,不一樣之處在於processRequest(Request)
方法會調用 Network 完成網絡請求,方法內部的執行大概分爲三步:
判斷 Request 的有效性
調用 Network 完成網絡請求,並利用 Request 將 NetworkResponse 轉換成 Response<T>
將 Request 和 Response 分發出去,並在須要緩存的時候將其加入緩存
分發的時候會調用 ResponseDelivery 的postResponse()
方法,這個方法的默認實現就是使用主線程的 Handler 將線程切換到主線程。
RequestQueue 提供了網絡請求的一些功能,但具體的數據仍是由 Request 和 Response 持有。
Request 是一個抽象類,共有兩個抽象方法:
/** * Subclasses must implement this to parse the raw network response and return an appropriate * response type. This method will be called from a worker thread. The response will not be * delivered if you return null. * * @param response Response from the network * @return The parsed response, or null in the case of an error */ protectedabstractResponse<T>parseNetworkResponse(NetworkResponseresponse); /** * Subclasses must implement this to perform delivery of the parsed response to their listeners. * The given response is guaranteed to be non-null; responses that fail to parse are not * delivered. * * @param response The parsed response returned by {@link * #parseNetworkResponse(NetworkResponse)} */ protectedabstractvoiddeliverResponse(Tresponse);複製代碼
parseNetworkReponse()
負責將 NetwrokResponse 轉換成 Response<T>,deliverResponse()
則負責將最終的數據分發出去。
除此以外 Request 裏就存有一些請求參數、優先級以及 URL 和請求方法等。
要使用 Request 就須要一個繼承 Request 的子類,在 Volley 中內置了四種 Request ,分別是 StringRequest、JsonRequest、ImageRequest 和 ClearCacheRequest 。前三者就是常規性的格式轉換 Request ,可以分別將請求到的原始數據轉換爲字符串、Json對象或 Bitmap ,以 StringRequest 爲例,它的parseNetworkResponse()
的實現以下:
protectedResponse<String>parseNetworkResponse(NetworkResponseresponse) { Stringparsed; try{ parsed=newString(response.data, HttpHeaderParser.parseCharset(response.headers)); } catch(UnsupportedEncodingExceptione) { // Since minSdkVersion = 8, we can't call // new String(response.data, Charset.defaultCharset()) // So suppress the warning instead. parsed=newString(response.data); } returnResponse.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); }複製代碼
而最後一個是功能性 Request ,它的做用就是清空緩存。
publicbooleanisCanceled() { // This is a little bit of a hack, but hey, why not. mCache.clear(); if(mCallback!=null) { Handlerhandler=newHandler(Looper.getMainLooper()); handler.postAtFrontOfQueue(mCallback); } returntrue; }複製代碼
在必定會調用的方法isCanceled()
中調用了 Cache 的clear()
方法,實現清空緩存,但大部分時候並不須要這麼麻煩,直接調用 Cache 的方法便可,考慮過是不是爲了使對 Cache 的操做都處於一個線程中,可是在 Volley 中,默認的 BasicDiskCache 是線程安全的,而對其的操做也是分佈在 CacheDispatcher 和 NetwrokDispatcher 中的(它們分別在不一樣的線程),因此拋去了這種考慮,那就實在想不出 ClearCacheRequest 的存在有什麼特殊的意義了。
Response 對應的類在 Volley 中共有兩個,分別是 NetworkResponse 和 Response<T>,前者對應着網絡請求獲得的實際響應,調用Network.performRequest()
以後獲得,而後再由Request.parseNetworkResponse()
對其進行格式轉換,通常須要與 Request 結合使用,每個自定義的 Request 用於解析一種數據類型,如 StringRequest 對應 String 類型,一樣,與之對應的 Response 返回的類型也是 String ,兩者在 StringRequest 的構造方法中統一:
publicStringRequest(
intmethod,
Stringurl,
Listener<String>listener,
@NullableErrorListenererrorListener);複製代碼
此處的 Listener 就是用戶設置的監聽器,當請求完成以後,會回調Listener.onResponse()
方法,將結果傳遞給調用方,且此時已通過 ResponseDelivery 處理,會在用戶設置的線程中回調。
Volley 的輕便性,我認爲體如今這幾個方面。
首先,Volley 爲網絡照片的展現提供了十分便利的方式——一個自定義的 NetworkImageView ,爲了實現這個 ImageView ,它還提供了相關的請求類,如 ImageLoader 、ImageRequest 等,利用這三個類,咱們能夠比較輕鬆的完成普通的網絡圖片的加載,固然,它本質上來講只是一個網絡請求庫,沒法提供像 Glide 那樣豐富的圖片加載功能,可是 Volley 勝在簡單方便。
其次,Volley 的 Request 這個特色讓咱們處理某種特定類型的數據變得十分簡單,當咱們實現了一個 XXXRequest ,它可以十分方便的工做,就像它 Request 本來就是爲此工做的同樣。
最後就是,Volley 這個庫自己的實現很簡單,總共不過40多個文件,卻構成了一個完整的網絡請求庫,不只如此,它也沒有拉下該有的功能,當咱們想快速實現某個功能的時候,它給全部組件都提供了默認的實現,當咱們想細化某一功能的時候,咱們也可使用自定義的組件實現某個功能。
另外一方便,對於一個想了解 Volley 內部實現過程的開發者來講也是十分友好的,Volley 有比較少的類,同時條理也比較清晰,在 Volley 中沒有過多的設計。
對於 Volley 的可擴展性,及用戶的自定義上來講,能夠從如下幾個接口一窺究竟。
首先,Cache 是一個接口,定義了諸如get()
、put()
等方法,這個類 Volley 基於文件讀寫實現了一個默認的 BasicDiskCache 類。Cache 會在 RequestQueue 的構造方法中傳進系統,並在以後傳遞給 CacheDispatcher 和 NetworkDispatcher ,因此,當這個默認的緩存不知足須要時,咱們徹底能夠自定義一個緩存類替換掉這個默認的,如實現一個內存緩存類,或者更有甚者,咱們能夠在get()
方法中截斷某些 Request 的網絡請求,返回一個由 Cache 本身生成的響應結果。
Network 只有一個方法,即根據 Request 進行網絡請求,獲得 NetworkResponse ,這個類的實例也是在 RequestQueue 的構造方法中傳入,與 Cache 同樣,Network 有本身的實現,可是咱們一樣可使用自定義的 Network 完成網絡的加載,如在某些狀況下可能要對 Request 進行額外的處理以後再進行網絡請求,如給全部 Request 加一個請求頭字段,即可以在自定義類的performRequest()
方法中對 Request 進行處理,不只如此,若是以爲 Volley 提供的默認的網絡請求功能有效率或者其餘方面的問題,還能夠在這個方法中使用其餘的庫進行加載,好比能夠在裏面再套一層 Okhttp ,可是這樣使用的問題就是要先把 Volley 的 Request 類轉換成 Okhttp 的 Request 類,並將 Okhttp 的 Response 再轉回 NetworkResponse 。
NetworkDispatcher 的數量,即同時在 Volley 中進行網絡請求的線程數量,也就是簡化的線程池,這樣即可以充分利用多線程的優點,更快地完成網絡請求。用戶能夠設置不一樣的數量以知足不一樣的需求,可是 threadPoolSize 的最小值是 1,也就是說任什麼時候候,Volley 中都至少有兩個線程在工做(還有一個 CacheDispatcher 所在的線程)。
這是一個接口,擁有三個方法,定義以下:
/** Parses a response from the network or cache and delivers it. */ voidpostResponse(Request<?>request, Response<?>response); /** * Parses a response from the network or cache and delivers it. The provided Runnable will be * executed after delivery. */ voidpostResponse(Request<?>request, Response<?>response, Runnablerunnable); /** Posts an error for the given request. */ voidpostError(Request<?>request, VolleyErrorerror);複製代碼
Request 是一個抽象類,這也就意味着它提供了必定的擴展功能,這在上文也提到過,用戶能夠自定義本身的 Request 用於將原始數據解析成特定的格式。大部分時候,咱們調用的 API 都返回某種格式的 json 數據,在網絡請求完成以後咱們每每會先調用一個方法解析數據,而在使用 Volley 的時候,就不須要再有這額外的操做了,直接定義一個 Request ,重寫它的parseNetworkResponse()
方法,那麼返回的 Response 數據便會是咱們須要的格式。
按照這個邏輯,用戶能夠輕鬆地替換掉默認的 ResponseDelivery ,本身實現一個可以切換到更多線程的功能,如 RxJava 中,能夠切換到所在線程、主線程或者是其餘的子線程等。
這些方法會在 CacheDispatcher 和 NetworkDispatcher 獲得結果以後調用,調用所在的線程是它們所在的子線程,因此爲了知足回到主線程工做的須要,ResponseDelivery 的默認實現 ExecutorDelivery 所作的事情就是將線程切換到主線程,利用的是一個主線程的 Handler ,向主線程提交了一個 Runnable ,在這個 Runnable 中,Request 會調用本身的deliverXXX()
方法將結果回調給本身的 Listener ,完成整個網絡請求。
Volley 中除了以上的這些以外,還有一個比較獨特的地方,即 HttpStack 和 BaseHttpStack 這幾個執行實際網絡請求的類上面。下面一一列出:
HttpStack
接口,聲明瞭一個網絡請求方法performRequest()
,它的返回參數是 org.apache.http.HttpResponse,已被標註棄用。
BaseHttpStack
抽象類,實現了 HttpStack 接口,並增長了一個新的方法executeRequest()
,它的返回參數是 HttpResponse 。
它實現了 HttpStack 的performRequest()
方法,可是在這個方法內部實際是調用executeRequest()
方法獲取到了 HttpResponse 實例,而後再將其轉換爲 org.apache.http.HttpResponse 返回。
AdapterHttpStack
繼承自 BaseHttpStack ,實例化時須要一個 HttpStack 實例,而後在實現executeRequest()
方法的時候使用了performRequest()
方法獲得 org.apache.http.HttpResponse 實例,而後將其轉換爲 HttpResponse 返回。
HurlStack
繼承自 BaseHttpStack ,使用 HttpUrlConnection 實現了executeRequest()
方法。
HttpClientStack
實現了 HttpStack 接口,使用 HttpClient 實現了performRequest()
方法,已被標註棄用。
通覽整個 HttpStack 家族的五個類,你是否明白了它們的工做原理?首先從兩個實現類 HurlStack 和 HttpClientStack 提及,它們分別使用了 HttpUrlConnection 和 HtttClient 實現,這兩個類都是 Android 原生提供的網絡請求類,可是它們適用於不一樣的 Android 版本,在 5.1 版本以後,HttpClient 便被 Google 棄用,並推薦使用 HttpUrlConnection 。因此 Volley 在使用這兩個類的時候也作了版本上的兼容。
最開始 Volley 僅提供了一個 HttpStack ,這個類默認就是使用 HttpClient 進行網絡請求,可是在以後的版本中 HttpClient 被棄用,因此又出現了 BaseHttpStack ,它提供了一個不須要依賴 HttpClient 的方法executeRequest()
,可是它並不能避免用戶繼續調用performRequest()
方法,因此重寫了這個方法在內部調用executeRequest()
,可是還有一個問題,若是用戶一直使用的都是自定義的 HttpStack 類,那在從新使用 BaseHttpStack 的時候必然還要從新實現一遍,因此,又有了 AdapterHttpStack 的存在,它繼承自 BaseHttpStack ,構造方法中傳入了 HttpStack 實例,可是與 BaseHttpStack 相反,它內部的邏輯是exetureRequest()
的實現依賴於performRequest()
的實現,用戶能夠將本身本來實現了 HttpStack 的類實例做爲參數傳遞給 AdapterHttpStack ,即可以獲得一個可工做的 BaseHttpStack 實例了。