Volley源碼分析 雖然在2017年,volley已經是一個逐漸被淘汰的框架,但其代碼短小精悍,網路架構設計巧妙,還是有很多值得學習的地方。 第一篇文章,分析了請求隊列的代碼,請求隊列也是我們使用Volley的關鍵一步。 第二篇文章會分析Dispatcher RequestQueue 創建Requ ...
Volley源碼分析
雖然在2017年,volley已經是一個逐漸被淘汰的框架,但其代碼短小精悍,網路架構設計巧妙,還是有很多值得學習的地方。
第一篇文章,分析了請求隊列的代碼,請求隊列也是我們使用Volley的關鍵一步。
第二篇文章會分析Dispatcher
RequestQueue
創建RequestQueue對象的方式是採用如下的代碼:
RequestQueue queue = Volley.newRequestQueue(getApplicationContext());
該隊列是用來發起Http請求。主要看newRequestQueue方法
該方法的核心實現是2個參數的方法
newRequestQueue(Context context, HttpStack stack)
該方法做的事情如下:
- 創建一個CacheDir目錄
- 創建一個userAgent,預設是volly/0,實際是包名 + 版本代碼。如果出異常,userAgent就是預設值
- 初始化stack,如果是Android2.3一下 就用HttpClientStack創建對象,HttpClientStack是用HttpClient實現的。如果是Android2.3以上,就用HurlStack創建對象。後面我們在分析這個類的作用。
- 用創建好的stack去初始化NetWork對象,即
Network network = new BasicNetwork(stack);
用創建好的CacheDir去初始化DiskBasedCache對象,從而完成RequestQueue對象的初始化,RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
- 調用queue對象的start方法。
下麵,我們看一下HttpStack的作用,以及DiskBasedCache和NetWork的作用,最後看一下start方法做了什麼。
HttpStack是一個介面,該介面只有兩個實現類,一個是HttpClientStack,另一個是HulStack。我們先不看具體的實現類,只看介面方法的聲明。
HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
該方法的作用是用來真正進行網路請求的,具體用那種http實現類去請求,就分成了HttpClient以及HttpUrl。
再看NetWork介面,該介面只有一個實現類,BasicNetWork,我們還是先不看具體的實現類,只看介面的方法聲明:
NetworkResponse performRequest(Request<?> request) throws VolleyError;
該方法的說明是執行一個指定的Request。
最後再看DiskBasedCache的功能。該類是Cache的一個具體的實現類,該類的功能是用緩存的文件存在硬碟中,預設的硬碟大小是5M。
上面的幾個介面的功能都說完了,通過其實現類的對象最終構造了RequestQueue。接下來我們看一下構造器方法的執行。
RequestQueue的成員變數以及方法
我們先看一下RequestQueue的成員變數都有什麼:
- mSequenceGenerator 該類型為AtomicInteger,其功能是統計請求的個數,採用原子類的整形。
- mWaitingRequests 該類型是一個HashMap,其功能是存儲request,key是cachekey。存儲的quest是重覆的request。
- mCurrentRequests 該類型是一個HashSet,其功能是存儲request,該request能被放入的條件是當前正在被分派或者在等待。
- mCacheQueue 該類型是PriorityBlockingQueue,其功能是存儲緩存的隊列。
- mNetworkQueue 該類型是PriorityBlockingQueue,其功能是正在進行工作的隊列
- DEFAULT_NETWORK_THREAD_POOL_SIZE 預設的分派器的線程 初始值為4
- mCache 該類型是Cache 功能是存儲響應報文的對象response
- mNetWork 該類型是NetWork 功能是進行網路請求。
- mDelivery 該類型是ResponseDelivery,其功能是分派response
- mDispatchers 該類型是NetworkDispatcher[] 其功能是NetWork分派器
- mCacheDispatcher 該類型是CacheDispatcher[] 其功能是Cache分派器
下麵我們繼續看構造方法:
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
無論幾個參數的構造方法,最終都會執行這個。上面在 new RequestQueue
中,我們的傳入的構造方法只有Cache和NetWork對象,這樣的話,threadPoolSize預設就是4,而delivery就是 new ExecutorDelivery(new Handler(Looper.getMainLooper()))
關於這個ExecutorDelivery這個後面在分析。
下麵,我們主要看RequestQueue的幾個關鍵方法:
- start
- stop
- cancel
- finsh
- add
stop方法
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (final NetworkDispatcher mDispatcher : mDispatchers) {
if (mDispatcher != null) {
mDispatcher.quit();
}
}
}
stop方法的全部代碼非常少,主要的做的事情如下:
- 如果CacheDispatcher不為空,則調用quit方法退出他。
- 如果NetWorkDispatcher不為空,則調用quit方法退出他。
關鍵就在於quit方法。
quit方法的代碼如下:
public void quit() {
mQuit = true;
interrupt();
}
CacheDispatcher是繼承與Thread。quit方法的作用是設置一個退出的標誌位,並且調用interrupt方法,這樣在run方法執行的過程中,由於線程已經中斷,會執行catch語句塊的內容,檢查標誌位,直接return。
下麵是去掉了與自身功能相關的代碼以後,剩下的部分。可以看出是很標準的線程退出寫法。
。
public void run() {
//執行初始化的操作,省略掉
while (true) {
try {
//執行業務代碼,省略掉
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
}
}
}
stop的方法分析完畢
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();
}
}
start方法比較簡潔,因此我在這裡放出全部的方法源碼。start方法主要做了以下幾件事情。
- 調用stop方法停止了正在運行的dispatchers。
- 創建CacheDispatcher,並且啟動它。
- 根據初始化的線程數量創建NetworkDispatcher,並啟動他。
stop方法上面已經分析了,就不在說了,先調用stop的目的就是先停止已經啟動的dispatcher。
創建CacheDispather,NetWorkDispatcher,他們其實就是一堆線程,調用他們的start方法。關於他們的分析,後面在具體介紹。
cancel方法
cancel方法實際上是cancalAll方法,但實際上最終都會調用cancelAll(RequestFilter filter) filter的作用實際上是過濾出要取消的request。然後調用request.cancel。
關於request類,這個放到後面的分析。
finish方法
<T> void finish(Request<T> request) {
// Remove from the set of requests currently being processed.
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
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);
}
}
}
}
finish方法是一個泛型方法,該finish方法執行了以下幾個事情:
- 從CurrentRequest中移除掉該request.
- 調用finishListener
- 判斷該request是否需要緩存,對需要緩存的request,將其他放到CacheQueue。
從上面的成員變數說明上,可以看出CurrentRequst實際上存儲正在分派或者執行的request,對於finish自然是從該隊列中移除。
/** Callback interface for completed requests. */
public interface RequestFinishedListener<T> {
/** Called when a request has finished processing. */
void onRequestFinished(Request<T> request);
}
該介面的目的是提供一個request結束以後回調的介面。
判斷request是否需要緩存,可以通過setShouldCache設置,然後從mWaitingReuqests中移除該key對應的隊列,然後將隊列加入cacheQueue。也就說,如果waitingQueue中還存在一樣的request,則全部移除掉。
add方法
add方法是添加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);
}
// 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();
if (mWaitingRequests.containsKey(cacheKey)) {
// 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);
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);
mCacheQueue.add(request);
}
return request;
}
}
將request關聯到當前的requestQueue,然後mCurrentRequeue添加添加該request,request設置加入隊列的次序,判斷該request是否需要緩存,如果不需要緩存,就直接加入netWorkQueue中,等待被執行。
如果需要緩存,就先判斷該WaitingRequest中是否有該request,如果有就得到該cacheKey對應的隊列,把該request加進去,如果沒有,就創建一個stagedRequests,將它加進去。然後將stagedRequests放入WaitiingRequest中。如果沒有cacheKey,waitingRequest就插入一個key為cacheKey,value為null的值進入,然後將request加入cacheQueue。
至此,RequestQueue的部分就分析完了,其主要的功能是根據傳入的Request來決定將該Request加入到那個Queue中,然後在通過Dispatcher進行調度。