標籤:
前面三篇文章從最基礎的TCP,HTTP協議理論開始,然後介紹了在Android的開發中所使用的HttpClient和HttpUrlConnection這兩種Http用戶端。在本文中,我們一起來學習一下在Android開發中經常使用的volley架構。首先,我們會從架構的角度瞭解一下整個架構的結構,然後從源碼的角度理解架構實現細節。
volley是Google在13年發布的一款Android非同步網路請求架構。volley有著鮮明的特點:適用於小資料量且頻繁的網路請求。這個特點特別適合於Android應用程式的網路操作。另外,我們從接下來將要介紹的架構中可以發現,volley採用了大量面向介面的設計,保證了整個架構的開放性和靈活性,可以根據不同的情況需求進行定製。同時,volley也提供了簡單的圖片載入工具。
在我們研究volley架構的結構之前,我們不妨自己先來思考一下如何?一款網路操作的架構。首先,我們根據實際需求設計網路操作請求(無非就是URL及其參數),然後使用多線程並發來執行和處理網路操作請求任務。從伺服器中取到資料後,將資料放在記憶體的cache中以便應用程式使用。
其實volley架構也是如此設計的,如所示:
我們可以看到,整個架構的結構分為4個部分:
1、volley使用一個請求隊列來管理各種網路請求Request。Request本身是一個描述請求的抽象類別,我們可以根據具體需求情況實現該抽象類別。volley也提供了一些Request子類,例如StringRequest,JsonRequest,ImageRequest等等。
2、在請求隊列RequestQueue中,有兩種個輪詢分發線程負責對請求任務進行分發調度。第一種是CacheDispatcher,負責調度資料儲存在cache中的請求任務;第二種是NetworkDispatcher,負責調度資料在遠端伺服器上的請求任務。另外,RequestQueue中還有一個叫作ResponseDelivery的介面,用於進行結果分發。請求隊列中的網路請求會首先被放入Cache任務隊列中,被CacheDispatcher線程調度。CacheDispatcher會試圖從cache中取出該任務所請求的資料,如果命中則交給ResponseDelivery解析該資料並返回給應用程式;如果未命中,或者緩衝失效等情況下,則將該請求任務加入到網路任務的隊列中,供NetworkDispatcher進程調度。NetworkDispatcher請求結束則將結果交給ResponseDelivery作後續的處理。
3、從上面的分析中我們可以看出,我們所請求的資料主要存在兩個個地方:Cache和網路。volley中分別使用Cache和HttpStack這兩個介面來描述它們以及所需執行的操作。其中,HttpStack負責處理http網路請求,volley中有兩種方式實現了HttpStack介面:基於 HttpURLConnection 的HurlStack 和 基於HttpClient 的HttpClientStack 。而Cache既可是基於SD卡,又可以基於記憶體。
4、通過上面所述的兩個介面可以擷取並操作我們請求的資料,這些資料主要分布在網路伺服器和本地記憶體或SD卡中。
volley維護了一個請求隊列來管理應用程式的網路請求,並採用了單例模式來保證一個應用程式只含有一個請求隊列。通常情況下,我們會繼承Application類,並通過newRequestQueue方法來建立一個請求隊列。從源碼中我們可以看出,在Android2.3以上使用了基於HttpUrlConnection的HurlStack處理網路請求,而2.3以下使用了基於httpclient的HttpClientStack來處理網路請求。這裡的原因在上一篇文章中我們提到過,這裡不再贅述。下面我們順著請求的提交—>處理—>完成 的這條線路來研究一下架構的內部細節。
針對不同的網路請求,我們可以實現Request這個抽象類別。該抽象類別描述了請求的url,方式,head,body以及優先順序等等資訊。然而volley已經為我們實現了大部分的子類來滿足各種需求。在Request的子類中,我們需要重寫兩個方法:
protected Response<T> parseNetworkResponse(NetworkResponse response) :用於將網路返回的位元組流解析為合適的資料類型。protected void deliverResponse(T response) :將解析好的資料傳遞給它的監聽回調。
另外,如果我們自訂Request,通常也會重寫getBody()方法來構建body內容;如果並未重寫getBody方法,那麼將會把getParams()方法返回的K-V值拼接起來的位元組碼作為body。
定義好了請求Request,接下來我們來通過源碼來研究一下RequestQueue這個類。RequestQueue作為volley架構的核心類,負責管理應用程式的網路請求。我們在使用volley進行網路的時候,向請求隊列提交了Request後發生了什麼呢?請看。
RequestQueue使用一個set來儲存一個未處理的請求。當我們提交一個請求後,RequestQueue會將該請求加入到這個集合中:
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
我們在上面已經提到過,我們主要從cache和網路來請求資料。於是,在RequestQueue中維護了兩個請求隊列:cache請求隊列CacheQueue和網路請求隊列NetworkQueue:
private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>();private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();
我們可以看到,這兩個隊列採用了優先阻塞隊列PriorityBlockingQueue來維護請求。每個新請求都會先放入cache隊列等待調度,只有在cache未命中或無效的情況下會被放入網路請求隊列。但是,如果一個請求在處理的同時,又有相同url的請求怎麼辦呢?顯然重複的請求只要到cache中去取就好了,無需再次進行網路請求,所以volley採用一個map來管理重複的請求,將它們暫時放入map中等待:
private final Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>();
提交了請求之後,接下來的工作就是對請求進行處理了,接下來我們來研究一下volley的調度策略。
對應於兩種請求隊列,分別使用CacheDispatcher和NetworkDispatcher兩個線程來調度分發,上面已經介紹過這兩種線程了,來看一下RequestQueue初始化和啟動的代碼:
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(); }}
從源碼中我們可以看出RequestQueue啟動了一個CacheDispatcher線程和多個NetworkDispatcher線程來完成調度分發,下面我們來研究一下這兩種線程。
CacheDispatcher
CacheDispatcher啟動後會不斷地輪詢mCacheQueue,隊列為空白時則等待;如果請求的cache未命中,到期或者無效,則會把該請求加入到mNetworkQueue中。當請求處理完成後則會將結果交給ResponseDelivery做最後的處理。具體流程如下:
NetworkDispatcher
NetworkDispatcher不斷輪詢mNetworkQueue取出請求去執行,隊列為空白則等待,請求處理結束則將結果傳遞給 ResponseDelivery 去執行後續處理,並判斷結果是否要進行緩衝。具體流程如下:
可以看到,無論是CacheDispatcher還是NetworkDispatcher,請求結束後都會將結果交給ResponseDelivery這個介面來做後續處理,在ResponseDelivery中主要有三個方法還傳遞請求結果或者錯誤:
public void postResponse(Request<?> request, Response<?> response) // 用於傳遞請求結果, request 和 response 參數分別表示請求資訊和返回結果資訊。public void postResponse(Request<?> request, Response<?> response, Runnable runnable) //用於傳遞請求結果,並在完成傳遞後執行 Runnablepublic void postError(Request<?> request, VolleyError error); //用於傳輸請求錯誤
當一個請求處理完成後,首先需要將當前處理集合mCurrentRequests 中的請求移除,然後在重複請求的mWaitingRequests中查詢是否有正在等待的重複請求,如果有則放入緩衝隊列中處理。
總結
通過從請求的建立—>提交—>處理—>完成 這條主線,本文簡單的介紹了volley架構的執行流程以及部分細節。volley的整個架構採用了大量的面向介面的設計,保證了使用的靈活性和架構的開放性。同時,volley又實現了很多介面來協助開發人員應對各種需求,在保證靈活性的同時也減少了很多繁瑣的工作。
Android網路編程隨想錄(四)