在Volley 源碼解析及對 Volley 的擴展
系列的第一篇文章中,介紹了一種通過繼承 StringRequest
、JsonObjectRequest
等自定義類,只需要重寫其中的一個方法,即可獲得網(wǎng)絡請求的耗時和網(wǎng)絡請求的結(jié)果,詳見第一篇文章。
在這篇文章中將對 Volley 的源碼進行解析,只有真正去研究 Volley 的源碼之后,才會發(fā)現(xiàn) Volley 設計的真是太精妙了。面向接口編程 在 Volley 中體現(xiàn)的非常徹底。
創(chuàng)建 RequestQueue
請求隊列對象
了解 Volley 用法的人都知道,使用 Volley 進行網(wǎng)絡請求的第一步就是創(chuàng)建一個請求隊列 RequestQueue
對象,創(chuàng)建 RequestQueue
對象的代碼如下所示:
RequestQueue mQueue = Volley.newRequestQueue(this);
在 Volley 中是通過靜態(tài)工廠方法的方式創(chuàng)建 RequestQueue
對象的,Volley
類的源碼如下所示:
public class Volley {
/** 默認的文件緩存路徑 */
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* 創(chuàng)建一個默認的請求隊列對象,并調(diào)用 {@link RequestQueue#start()} 方法啟動它。
*
* @param context 一個 {@link Context} 對象用于創(chuàng)建緩存文件對象
* @param stack 一個用于執(zhí)行網(wǎng)絡請求的 {@link HttpStack} 對象,若為 null,則使用默認的網(wǎng)絡請求對象
* @return 一個已經(jīng)啟動的 {@link RequestQueue} 對象
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// 在 Android SDK 9 之前,HttpUrlConnection 有 Bug,不可靠,所以使用 HttpClient
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
/**
* 創(chuàng)建一個默認的請求隊列對象,并調(diào)用 {@link RequestQueue#start()} 方法啟動它。
*
* @param context 一個 {@link Context} 對象用于創(chuàng)建緩存文件對象
* @return 一個已經(jīng)啟動的 {@link RequestQueue} 對象
*/
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
}
- 在
Volley
類中有兩個重載的靜態(tài)方法
public static RequestQueue newRequestQueue(Context context);
public static RequestQueue newRequestQueue(Context context, HttpStack stack);
第一個方法的實現(xiàn)調(diào)用了第二個方法,傳入的 HttpStack
對象為 null
- 通過包名和版本號創(chuàng)建
userAgent
對象 - 如果傳入的
HttpStack
對象為null
, 則根據(jù) SDK 版本號創(chuàng)建默認的HttpStack
對象,若 SDK 版本號大于等于 9,則創(chuàng)建HurlStack
對象(內(nèi)部使用HttpUrlConnection
實現(xiàn));否則創(chuàng)建HttpClientStack
對象(內(nèi)部使用HttpClient
實現(xiàn))。 - 通過已經(jīng)創(chuàng)建的
HttpStack
對象創(chuàng)建一個Network
具體實現(xiàn)類BasicNetwork
的對象 - 通過
BasicNetwork
對象和DiskBasedCache
磁盤緩存對象創(chuàng)建一個RequestQueue
對象,并啟動。
創(chuàng)建一個 Request
對象
Request
是代表網(wǎng)絡請求的一個抽象類,其中有兩個抽象方法,子類必須實現(xiàn)這兩個方法:
/**
* 子類必須實現(xiàn)此方法,用于解析網(wǎng)絡請求的響應,并返回合適的類型對象。這個方法會在一個
* 工作線程中被調(diào)用(即不會在 UI 線程中調(diào)用此方法),如果此方法返回 null,則結(jié)果并不會被發(fā)送。
*
* @param response 來自于網(wǎng)絡請求的響應
* @return 解析的結(jié)果,如果發(fā)生錯誤則返回 null
*/
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
/**
* 子類必須實現(xiàn)這個方法,把解析的結(jié)果發(fā)送給監(jiān)聽器。其中 T 類型的參數(shù) response 要保證
* 不可以為 null,如果解析失敗,則解析的結(jié)果不會通過此方法發(fā)送。
*
* @param response 通過 {@link #parseNetworkResponse(NetworkResponse)} 方法解析的結(jié)果
*/
abstract protected void deliverResponse(T response);
- 默認實現(xiàn)
Request
的子類有:StringRequest
、JsonObjectRequest
、
JsonArrayRequest
和ImageRequest
- 我們也可以自定義一個實現(xiàn)
Request
的類,實現(xiàn)上面兩個方法,將其加入到網(wǎng)絡請求隊列中進行網(wǎng)絡請求。在下一篇博客中將會舉兩個自定義Request
的例子 - Volley 中包括 8 種 Http 網(wǎng)絡請求方式:
GET
、POST
、PUT
、DELETE
、HEAD
、
OPTIONS
、TRACE
、PATCH
-
Request
類中包含了網(wǎng)絡請求的url
,請求方式,請求Header
,請求Body
和請求的優(yōu)先級等信息。 - 以下三個方法也經(jīng)常被子類重寫
/**
* 返回一個 Map 類型的參數(shù),為這個請求添加網(wǎng)絡請求頭信息 Http Header。
* 最常用的就是可以把 Cookie 信息通過此方法添加
*/
public Map<String, String> getHeaders() throws AuthFailureError {
return Collections.emptyMap();
}
/**
* 返回一個字節(jié)數(shù)組的對象作為 POST 或 PUT 請求的 Body 內(nèi)容。
*
* 當重寫此方法時,也需要考慮重寫 {@link #getBodyContentType()} 方法
*/
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
/**
* 在 {@link #getBody()} 沒有被重寫的情況下,可以通過此方法返回一個
* Map 類型的參數(shù),用于構(gòu)建 POST 或 PUT 請求方式的 Body 內(nèi)容
*
* 注意:也可以通過直接重寫 {@link #getBody()} 方法自定義 Body 數(shù)據(jù)。
*/
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}
RequestQueue 源碼分析
RequestQueue
是 Volley 中的核心類,主要用于處理添加進來的網(wǎng)絡請求。
在本小節(jié)中將會分三部分介紹 RequestQueue
的類,分別是:RequestQueue
中的主要屬性、RequestQueue
類的構(gòu)造方法和 RequestQueue
的主要方法。
RequestQueue 中的主要屬性
/**
* 維護了一個等待請求的集合,如果有一個請求正在被處理并且可以被緩存,如果有新的相同
* URL 請求被添加進來以后,則會新的請求則會進入此集合中。此集合主要是為了避免相同的
* 且不必要的網(wǎng)絡請求
*/
private final Map<String, Queue<Request<?>>> mWaitingRequests =
new HashMap<String, Queue<Request<?>>>();
/**
* 正在被此 RequestQueue 處理的請求的集合,如果一個請求正在等待被處理或者正在被
* 某個調(diào)度線程處理,則它會在此集合中
*/
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
/** 緩存請求隊列,在此隊列中的請求,將通過緩存獲取數(shù)據(jù) */
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
/** 網(wǎng)絡請求隊列,在此隊列中的請求,將通過網(wǎng)絡向服務器發(fā)送請求獲取數(shù)據(jù) */
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
在 RequestQueue
中有兩個 基于優(yōu)先級 Request
的隊列:mCacheQueue
緩存請求隊列和 mNetworkQueue
網(wǎng)絡請求隊列
RequestQueue 的構(gòu)造方法
/** 默認的網(wǎng)絡請求調(diào)度線程數(shù)量 4 */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/** Cache 接口,用戶獲取和緩存響應結(jié)果,默認的實現(xiàn)類是 DiskBasedCache */
private final Cache mCache;
/** Network 接口,用于執(zhí)行網(wǎng)絡請求,默認的實現(xiàn)類是 BasicNetwork */
private final Network mNetwork;
/** 響應分發(fā)器,默認的實現(xiàn)類是 ExecutorDelivery */
private final ResponseDelivery mDelivery;
/** 網(wǎng)絡調(diào)度線程數(shù)組,NetworkDispatcher是 {@link Thread} 的子類*/
private NetworkDispatcher[] mDispatchers;
/** 緩存調(diào)度線程,是{@link Thread} 的子類*/
private CacheDispatcher mCacheDispatcher;
/**
* 創(chuàng)建工作線程池,不調(diào)用 {@link #start()} 方法,就不會開始開始工作,所以創(chuàng)建完請求隊列以后,必須調(diào)用{@link #start()}
*
* @param cache 向磁盤持久化響應結(jié)果的緩存對象
* @param network 執(zhí)行 Http 請求的對象
* @param threadPoolSize 網(wǎng)絡請求調(diào)度線程的數(shù)量
* @param delivery 一個負責分發(fā)響應結(jié)果和異常的分發(fā)器
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
/**
* 調(diào)用 {@link #RequestQueue(Cache, Network, int, ResponseDelivery)} 實現(xiàn)
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
/**
* 調(diào)用 {@link #RequestQueue(Cache, Network, int)} 實現(xiàn)
*/
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
- 有三個構(gòu)造方法,最終調(diào)用的是
RequestQueue(Cache, Network, int,ResponseDelivery)
這個構(gòu)造方法 - 創(chuàng)建一個
ExecutorDelivery
對象并賦值給mDelivery
,其中在ExecutorDelivery
構(gòu)造函數(shù)中傳入的Handler
對象中的Looper
對象是主線程的,這樣使用mDelivery
發(fā)送的請求響應結(jié)果或者異常就被發(fā)送到主線程中了 - 創(chuàng)建一個
NetworkDispatcher
類型的數(shù)組對象mDispatchers
,默認長度是4 - 可以看到所依賴的屬性
mCache
、mNetwork
、mDelivery
都是接口類型的,而不是具體的實現(xiàn)類,這充分體現(xiàn)了面向接口編程的思想
RequestQueue 中的主要方法
還記得在 Volley
類中的 newRequestQueue(Context, HttpStack)
創(chuàng)建完成 RequestQueue
對象 queue
以后,還調(diào)用的了 queue.start()
方法,start()
相關(guān)方法如下所示:
/**
* 啟動在此隊列中的線程
*/
public void start() {
stop(); // 在啟動之前,需要確保現(xiàn)在正在運行 mCacheDispatcher 線程和 mDispatchers[] 中的線程被終止
// 創(chuàng)建緩存調(diào)度線程并啟動
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// 根據(jù) mDispatchers[] 的長度,創(chuàng)建對應數(shù)量的網(wǎng)絡調(diào)度線程添加進 mDispatchers[] 并啟動
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
/**
* 停止緩存調(diào)度線程 mCacheDispatcher 和網(wǎng)絡調(diào)度線程 mDispatchers[]
*/
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
-
NetworkDispatcher
和CacheDispatcher
都是Thread
的子類,都是線程,創(chuàng)建完該對象以后都需要進行調(diào)用start()
方法啟動該線程。到這塊兒的代碼,意識到有必要看一下NetworkDispatcher
和CacheDispatcher
這兩個類的代碼了,先不著急,我們先分析完RequestQueue
的代碼。
通過 Volley 進行網(wǎng)絡請求時,創(chuàng)建完網(wǎng)絡請求之后,需要將網(wǎng)絡請求通過 RequestQueue.add(Request)
方法,將網(wǎng)絡請求添加進網(wǎng)絡請求隊列,那么來分析下 add(Request)
方法,這個方法是 RequestQueue
中非常重要的一個方法。
/** 用于為請求生成一個自動增長的序列號 */
private AtomicInteger mSequenceGenerator = new AtomicInteger();
.....
/**
* 得到一個序列號
*/
public int getSequenceNumber() {
return mSequenceGenerator.incrementAndGet();
}
.....
/**
* 向請求隊列中添加一個請求
* @param request 向服務器發(fā)送的請求
* @return 已經(jīng)發(fā)送經(jīng)過處理的請求
*/
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 設置請求隊列,并將其添加進 mCurrentRequests 隊列中
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// Process requests in the order they are added.
// 根據(jù)他們添加進來的順序設置唯一的序列號
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// If the request is uncacheable, skip the cache queue and go straight to the network.
// 如果該請求 request 不可以緩存的,則跳過緩存隊列,直接進入網(wǎng)絡請求隊列
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.
// 首先根據(jù) cacheKey(其實就是url)判斷 mWaitingRequests 中是否有相同的請求正在進行,如果有,則將其添加進 mWaitingRequests 隊列中
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 中沒有相同的請求正在進行,則在 mWaitingRequests 插入一個 null 值
mWaitingRequests.put(cacheKey, null);
// 將該 request 添加進 mCacheQueue 隊列中
mCacheQueue.add(request);
}
return request;
}
}
- 通過
add(Request)
添加一個網(wǎng)絡請求,首先需要將該請求request
添加到mCurrentRequests
隊列中 - 如果該請求不走緩存,則直接將該請求
request
添加到網(wǎng)絡請求隊列mNetworkQueue
中,結(jié)束該方法 - 如果該請求
request
可以走緩存,根據(jù)cacheKey
(其實就是url
)判斷mWaitingRequests
中是否有相同的請求正在進行,如果有,則將其添加進mWaitingRequests
隊列中,如果沒有則在mWaitingRequests
中添加值為null
的值,并將其添加進緩存請求隊列mCacheQueue
中 -
RequestQueue.add(Request)
方法的流程圖(該圖出自 Volley 源碼解析)如下所示:
RequestQueue
還有一個常用的方法:RequestQueue.cancelAll(Object)
/**
* 一個在 {@link RequestQueue#cancelAll(RequestFilter)} 方法中使用的判斷或過濾接口
*/
public interface RequestFilter {
public boolean apply(Request<?> request);
}
/**
* 將此隊列中符合 filter 條件的所有請求取消
* @param filter 使用的過濾條件
*/
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}
/**
* 通過給定的 tag 取消在此隊列中所有 tag 相同的請求,tag絕對不可以為 bull
*/
public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> request) {
return request.getTag() == tag;
}
});
}
- 一般我都會重寫每個請求對象
***Request
的setTag()
方法返回一個該請求的TAG
,在合適的地方(比如:Activity.onDestory()
方法中)通過cancelAll(Object)
取消該TAG
對應的請求,以防止發(fā)生意外(比如:內(nèi)存泄露)。
NetworkDispatcher 源碼分析
由于 NetworkDispatcher
源碼也并不算長,只有100+行,直接上源碼,里面的注釋也很詳細了,后面會配有相應的說明和流程圖。
public class NetworkDispatcher extends Thread {
/** 請求服務器的網(wǎng)絡請求隊列 */
private final BlockingQueue<Request<?>> mQueue;
/** 處理網(wǎng)絡請求的實現(xiàn) Network 接口類的對象 */
private final Network mNetwork;
/** 寫緩存的對象 */
private final Cache mCache;
/** 用于發(fā)送響應和異常的分發(fā)器 */
private final ResponseDelivery mDelivery;
/** 用于標志此線程是否中斷 Used for telling us to die. */
private volatile boolean mQuit = false;
/**
* 創(chuàng)建一個網(wǎng)絡調(diào)度線程,必須調(diào)用 {@link #start()} 啟動此線程
*
* @param queue 網(wǎng)絡請求隊列
* @param network 執(zhí)行網(wǎng)絡請求的 Network 接口實現(xiàn)類
* @param cache 將響應寫進緩存的 Cache 接口實現(xiàn)類
* @param delivery 用于分發(fā)請求結(jié)果的分發(fā)器
*/
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
/**
* 強制此調(diào)度線程立即停止。如果在隊列中仍然有請求,它們不能保證一定會被處理
*/
public void quit() {
mQuit = true;
interrupt();
}
......
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
// 記錄網(wǎng)絡請求開始的時間
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// Take a request from the queue.
// 從隊列中取出一個網(wǎng)絡請求
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// If the request was cancelled already, do not perform the
// network request.
// 如果該請求已經(jīng)被取消,則不會進行網(wǎng)絡請求
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// Perform the network request.
// 執(zhí)行網(wǎng)絡請求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
// 如果服務器返回的 304,并且該請求之前已經(jīng)得到過并發(fā)送過響應結(jié)果,則響應結(jié)果可以復用,沒必要進行新的網(wǎng)絡請求,結(jié)束本次循環(huán)
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
// 在工作線程中解析得到的網(wǎng)絡請求響應結(jié)果
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
// 根據(jù) request 的 {@link #shouldCache()} 方法判斷此請求是否需要進行緩存,如果需要進行緩存處理,則將其放到 mCache 中
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back
// 將響應通過 mDelivery 發(fā)送到 UI 線程中
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
// 如果拋出 VolleyError 異常,則將網(wǎng)絡請求耗時通過{@link volleyError#setNetworkTimeMs(long)}
// 放進 volleyError 中,并通過 mDelivery 將異常發(fā)送出去
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
}
catch (Exception e) {
// 若發(fā)生其他異常,則生成 VolleyError 對象,并將網(wǎng)絡請求耗時放進 volleyError 對象中,
// 并通過 mDelivery 將異常發(fā)送出去
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}
}
- 在
run()
方法中有一個while(true){ ...... }
代碼段,可見啟動此線程以后,它就進入一直在循環(huán)的狀態(tài),不斷的從網(wǎng)絡請求隊列mQueue
中取出網(wǎng)絡請求并執(zhí)行,除非通過quit()
方法改變mQuit
成為true
,該方法才會停止 -
NetworkDispatcher
類中所依賴的屬性,mNetwork
、mCache
和mDelivery
都是接口類型的,而不是具體的實現(xiàn)類,這也充分體現(xiàn)了面向接口編程的思想 - 在上面的代碼片段中,注釋已經(jīng)很清楚了。下面是一張
NetworkDispatcher
進行網(wǎng)絡請求的流程圖,出自 Volley 源碼解析。
CacheDispatcher 源碼分析
public class CacheDispatcher extends Thread {
private static final boolean DEBUG = VolleyLog.DEBUG;
/** 緩存請求隊列 The queue of requests coming in for triage. */
private final BlockingQueue<Request<?>> mCacheQueue;
/** 網(wǎng)絡請求隊列 The queue of requests going out to the network. */
private final BlockingQueue<Request<?>> mNetworkQueue;
/** 緩存接口 The cache to read from. */
private final Cache mCache;
/** 請求結(jié)果分發(fā)類 For posting responses. */
private final ResponseDelivery mDelivery;
/** 用于標志此線程是否中斷 Used for telling us to die. */
private volatile boolean mQuit = false;
/**
* 創(chuàng)建一個 緩存調(diào)度線程,必須調(diào)用 {@link #start()} 方法,此線程才會開始工作
*
* @param cacheQueue 緩存請求隊列
* @param networkQueue 網(wǎng)絡請求隊列
* @param cache 處理緩存的對象
* @param delivery 分發(fā)響應的結(jié)果
*/
public CacheDispatcher(
BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}
/**
* 強制此線程立即停止,如果在隊列中有請求,則不能保證請求一定會被處理
*/
public void quit() {
mQuit = true;
interrupt();
}
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
// 初始化緩存
mCache.initialize();
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
// 從緩存隊列中取一個請求,如果沒有可用的則阻塞
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
// 如果緩存請求已經(jīng)被取消,則不用處理它
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
// 通過 request.getCacheKey() 從緩存中取出對應的緩存記錄
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
// 如果沒有得到緩存結(jié)果,則將該請求加入到網(wǎng)絡請求隊列中
mNetworkQueue.put(request);
continue;
}
// If it is completely expired, just send it to the network.
// 如果緩存已經(jīng)過期,則將該請求添加進網(wǎng)絡請求隊列中
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
// 如果得到正確的緩存結(jié)果,則生成對應的響應
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
// 如果緩存記錄不需要更新,則直接通過 mDelivery 將結(jié)果發(fā)送到 UI 線程中
mDelivery.postResponse(request, response);
} else {
// Soft-expired cache hit. We can deliver the cached response,
// but we need to also send the request to the network for
// refreshing.
// 還存在這樣一種情況,緩存記錄存在,但是它約定的生存時間已經(jīng)到了(還未完全過期,
// 叫軟過期),可以將其發(fā)送到主線程去更新
// 但同時,也要從網(wǎng)絡中更新它的數(shù)據(jù)
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
// 將其傳回主線程的同時,將請求放到Network隊列中
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
}
}
}
-
CacheDispatcher
類和NetworkDispatcher
類的代碼很相似,都是Thread
的子類,創(chuàng)建該類的對象以后都需要通過start()
方法啟動它 -
CacheDispatcher
所依賴的對象mCache
和mDelivery
都是接口類型的,而不是具體的實現(xiàn)類,這也是面向接口編程思想的體現(xiàn) - 下面是一張
CacheDispatcher
進行緩存請求的流程圖,同樣出自 Volley 源碼解析。
BasicNetwork 源碼分析
BasicNetwork
是 Network
接口的實現(xiàn)類,Network
接口是執(zhí)行網(wǎng)絡請求的接口,其中只有一個方法 performRequest(Request)
,該方法由于執(zhí)行網(wǎng)絡請求并返回 NetworkResponse
類型的請求結(jié)果,那來看一下在 BasicNetwork
中該方法是怎么實現(xiàn)的
public class BasicNetwork implements Network {
protected final HttpStack mHttpStack;
......
/**
* 通過 {@link HttpStack#performRequest(Request, Map)} 方法執(zhí)行網(wǎng)絡請求,將得到的網(wǎng)絡請求響應包裝成 NetworkResponse 類型的對象并將其返回
*/
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
// 記錄請求開始的時間
long requestStart = SystemClock.elapsedRealtime();
while (true) {
// 請求響應的對象引用
HttpResponse httpResponse = null;
// 請求響應中的 body
byte[] responseContents = null;
// 請求響應中的 header
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// 請求頭
Map<String, String> headers = new HashMap<String, String>();
// 將請求中添加的 CacheEntry 添加到請求頭中,詳見 {@link addCacheHeaders(Map<String, String>, Cache.Entry)} 方法
addCacheHeaders(headers, request.getCacheEntry());
// 通過 mHttpStack.performRequest(Request, Map<String, String>) 方法執(zhí)行具體的網(wǎng)絡請求,并得到請求的響應
httpResponse = mHttpStack.performRequest(request, headers);
// 請求響應中的狀態(tài)行信息對象
StatusLine statusLine = httpResponse.getStatusLine();
// 請求響應中的 狀態(tài)碼
int statusCode = statusLine.getStatusCode();
// 響應中的響應頭 Header[] 轉(zhuǎn)換成 Map<String, String> 的形式
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
// 如果響應中狀態(tài)碼是 304,則表示請求的內(nèi)容在服務器端沒有更改,使用本地的緩存即可
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
// 取出緩存 entry 對象
Entry entry = request.getCacheEntry();
if (entry == null) {
// 如果 entry 對象為 null,則返回一個 entry 為 null 的 NetworkResponse 對象
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
// 即使返回 304 的響應,但是真正的響應中的響應頭包括兩部分信息:當前返回的響應頭和緩存中已經(jīng)緩存的響應頭
entry.responseHeaders.putAll(responseHeaders);
// 使用緩存對象 entry 生成一個 NetworkResponse 對象并返回
// 將請求耗時放入 NetworkResponse 對象中
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// Some responses such as 204s do not have content. We must check.
// 有一些響應(比如:204)并沒有 body 內(nèi)容,所以必須進行檢查
if (httpResponse.getEntity() != null) {
// 將響應中的 HttpEntity 對象轉(zhuǎn)換成 byte[] 類型的 responseContents 對象,詳見 {@link entityToBytes(HttpEntity)} 方法
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
// 如果響應中的 HttpEntity 為空,也要給 responseContents 對象賦值
responseContents = new byte[0];
}
// if the request is slow, log it.
// 如果請求的耗時太久則打印請求相關(guān)的信息,詳見 {@link logSlowRequests(long, Request ,byte[], StatusLine)} 方法
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
// 如果響應狀態(tài)碼超出 200-299 的范圍,則拋出 IOException 異常
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
// 如果響應狀態(tài)碼在 200-299 之內(nèi),則生成 NetworkResponse 對象并返回
// 將請求耗時放入 NetworkResponse 對象中
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
// 處理 SocketTimeout,重復請求
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
// 處理 ConnectTimeout,重復請求
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
// 處理 MalformedURLException,重復請求
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
// 處理 IOException,重復請求
int statusCode;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
NetworkResponse networkResponse;
if (responseContents != null) {
networkResponse = new NetworkResponse(statusCode, responseContents,
responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
statusCode == HttpStatus.SC_FORBIDDEN) {
attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));
} else if (statusCode >= 400 && statusCode <= 499) {
// Don't retry other client errors.
throw new ClientError(networkResponse);
} else if (statusCode >= 500 && statusCode <= 599) {
if (request.shouldRetryServerErrors()) {
attemptRetryOnException("server",
request, new ServerError(networkResponse));
} else {
throw new ServerError(networkResponse);
}
} else {
// 3xx? No reason to retry.
throw new ServerError(networkResponse);
}
} else {
attemptRetryOnException("network", request, new NetworkError());
}
}
}
}
......
}
-
BasicNetwork
類中最重要的一個屬性就是mHttpStack
是HttpStack
類型的,HttpStack
也是接口類型的,它是具體的執(zhí)行網(wǎng)絡請求的接口,在 Volley 中有兩個實現(xiàn)了HttpStack
接口的類:HurlStack
和HttpClientStack
,HurlStack
內(nèi)部是使用HttpUrlConnection
實現(xiàn)的,而HttpClientStack
內(nèi)部是使用HttpClient
實現(xiàn)的 - 在
BasicNetwork
中,對響應狀態(tài)碼為304
、204
等特殊情況做了一定的處理,如果狀態(tài)碼在200-299
之外則拋出IOException
,在200-299
之內(nèi)則生成NetworkResponse
對象并返回,并對各種異常SocketTimeoutException
和ConnectTimeoutException
等異常做了特殊的處理 - 在
performRequest(Request)
中起始的位置記錄請求開始的時間,在生成NetworkResponse
對象或者拋出VolleyError
異常中都代碼網(wǎng)絡請求的時間,這是第一篇博客中請求耗時的原始值
ExecutorDelivery 源碼分析
ExecutorDelivery
是 ResponseDelivery
接口的實現(xiàn)類,ResponseDelivery
接口主要有三個方法:
public interface ResponseDelivery {
/**
* 解析一個來自網(wǎng)絡或者緩存的響應結(jié)果并發(fā)送
*/
public void postResponse(Request<?> request, Response<?> response);
/**
* 解析一個來自網(wǎng)絡或者緩存的響應結(jié)果并發(fā)送,提供的 Runnable 對象會在發(fā)送完結(jié)果之后被執(zhí)行
*/
public void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/**
* 向該 request 對象發(fā)送一個異常
*/
public void postError(Request<?> request, VolleyError error);
}
接著看一下 ExecutorDelivery
實現(xiàn)類的代碼
public class ExecutorDelivery implements ResponseDelivery {
/** 向主線程中發(fā)送結(jié)果的線程池對象 */
private final Executor mResponsePoster;
/**
* ExecutorDelivery 的構(gòu)造方法
* @param handler {@link Handler} 對象決定了是向哪個線程發(fā)送結(jié)果
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
// 使用提供的 handler 對象實現(xiàn)一個 Executor 對象
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
/**
* ExecutorDelivery 的構(gòu)造方法
* @param executor 用于發(fā)送結(jié)果的線程池
*/
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
// 根據(jù)傳進來的 request 、response 和 runnable 對象,生成一個 ResponseDeliveryRunnable 對象,并使用 mResponsePoster 線程池運行
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
/**
* 一個用于將網(wǎng)絡請求的響應結(jié)果發(fā)送到主線程的線程對象
*/
@SuppressWarnings("rawtypes")
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
// If this request has canceled, finish it and don't deliver.
// 如果請求已經(jīng)被取消,則結(jié)束它并不會發(fā)送該請求的結(jié)果
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
// 視情況通過不同的方法發(fā)送正確的響應或異常
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
// 添加標志
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
// 如果 mRunnable 對象不為 null, 則執(zhí)行它
if (mRunnable != null) {
mRunnable.run();
}
}
}
}
- 在
ExecutorDelivery
的構(gòu)造方法中,需要傳入一個Handler
對象,這個Handler
對象是非常重要的。都知道每個Handler
對象都會持有一個Looper
對象,該對象決定了Handler
發(fā)送的消息或者任務在那個線程中處理。在分析RequestQueue
源碼時,是這樣new ExecutorDelivery(new Handler(Looper.getMainLooper()))
生成的ExecutorDelivery
默認的對象,所以默認情況下,通過ExecutorDelivery
對象發(fā)送的消息都是在主線程中處理的。這也符合我們的習慣,具體的網(wǎng)絡請求結(jié)果都是在 UI 線程中直接處理,這樣更方便一些 - 在
ExecutorDelivery
中的內(nèi)部類ResponseDeliveryRunnable
,是非常重要的,在它的run()
方法中,如果成功則調(diào)用request
的deliverResponse(T)
方法,否則調(diào)用deliverError(VolleyError)
方法。這里的deliverResponse(T)
方法內(nèi)部最終會回調(diào)我們在構(gòu)建Request
時設置的Response.Listener
對象onResponse(T)
方法的。
至此,關(guān)于Volley 源碼解析及對 Volley 的擴展
系列的第二篇文章就結(jié)束了,從這邊文章中也可以知道為什么都說 Volley
具有很強的擴展性,因為很多地方依賴的屬性都是接口,而不是具體的實現(xiàn)類。接下來在第三篇文章中就會對 Volley
做一些擴展。如果有什么問題歡迎指出。我的工作郵箱:jiankunli24@gmail.com
參考資料:
Volley學習筆記之簡單使用及部分源碼詳解 -- Yongyu