在Volley 源碼解析及對(duì) Volley 的擴(kuò)展
系列的第一篇文章中崎脉,介紹了一種通過繼承 StringRequest
、JsonObjectRequest
等自定義類,只需要重寫其中的一個(gè)方法琐馆,即可獲得網(wǎng)絡(luò)請(qǐng)求的耗時(shí)和網(wǎng)絡(luò)請(qǐng)求的結(jié)果,詳見第一篇文章恒序。
在這篇文章中將對(duì) Volley 的源碼進(jìn)行解析瘦麸,只有真正去研究 Volley 的源碼之后,才會(huì)發(fā)現(xiàn) Volley 設(shè)計(jì)的真是太精妙了歧胁。面向接口編程 在 Volley 中體現(xiàn)的非常徹底滋饲。
創(chuàng)建 RequestQueue
請(qǐng)求隊(duì)列對(duì)象
了解 Volley 用法的人都知道厉碟,使用 Volley 進(jìn)行網(wǎng)絡(luò)請(qǐng)求的第一步就是創(chuàng)建一個(gè)請(qǐng)求隊(duì)列 RequestQueue
對(duì)象,創(chuàng)建 RequestQueue
對(duì)象的代碼如下所示:
RequestQueue mQueue = Volley.newRequestQueue(this);
在 Volley 中是通過靜態(tài)工廠方法的方式創(chuàng)建 RequestQueue
對(duì)象的屠缭,Volley
類的源碼如下所示:
public class Volley {
/** 默認(rèn)的文件緩存路徑 */
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* 創(chuàng)建一個(gè)默認(rèn)的請(qǐng)求隊(duì)列對(duì)象箍鼓,并調(diào)用 {@link RequestQueue#start()} 方法啟動(dòng)它。
*
* @param context 一個(gè) {@link Context} 對(duì)象用于創(chuàng)建緩存文件對(duì)象
* @param stack 一個(gè)用于執(zhí)行網(wǎng)絡(luò)請(qǐng)求的 {@link HttpStack} 對(duì)象呵曹,若為 null款咖,則使用默認(rèn)的網(wǎng)絡(luò)請(qǐng)求對(duì)象
* @return 一個(gè)已經(jīng)啟動(dòng)的 {@link RequestQueue} 對(duì)象
*/
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)建一個(gè)默認(rèn)的請(qǐng)求隊(duì)列對(duì)象,并調(diào)用 {@link RequestQueue#start()} 方法啟動(dòng)它跨新。
*
* @param context 一個(gè) {@link Context} 對(duì)象用于創(chuàng)建緩存文件對(duì)象
* @return 一個(gè)已經(jīng)啟動(dòng)的 {@link RequestQueue} 對(duì)象
*/
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
}
- 在
Volley
類中有兩個(gè)重載的靜態(tài)方法
public static RequestQueue newRequestQueue(Context context);
public static RequestQueue newRequestQueue(Context context, HttpStack stack);
第一個(gè)方法的實(shí)現(xiàn)調(diào)用了第二個(gè)方法富腊,傳入的 HttpStack
對(duì)象為 null
- 通過包名和版本號(hào)創(chuàng)建
userAgent
對(duì)象 - 如果傳入的
HttpStack
對(duì)象為null
梳凛, 則根據(jù) SDK 版本號(hào)創(chuàng)建默認(rèn)的HttpStack
對(duì)象寇壳,若 SDK 版本號(hào)大于等于 9凿试,則創(chuàng)建HurlStack
對(duì)象(內(nèi)部使用HttpUrlConnection
實(shí)現(xiàn))庭猩;否則創(chuàng)建HttpClientStack
對(duì)象(內(nèi)部使用HttpClient
實(shí)現(xiàn))。 - 通過已經(jīng)創(chuàng)建的
HttpStack
對(duì)象創(chuàng)建一個(gè)Network
具體實(shí)現(xiàn)類BasicNetwork
的對(duì)象 - 通過
BasicNetwork
對(duì)象和DiskBasedCache
磁盤緩存對(duì)象創(chuàng)建一個(gè)RequestQueue
對(duì)象筏餐,并啟動(dòng)意鲸。
創(chuàng)建一個(gè) Request
對(duì)象
Request
是代表網(wǎng)絡(luò)請(qǐng)求的一個(gè)抽象類筒繁,其中有兩個(gè)抽象方法许饿,子類必須實(shí)現(xiàn)這兩個(gè)方法:
/**
* 子類必須實(shí)現(xiàn)此方法阳欲,用于解析網(wǎng)絡(luò)請(qǐng)求的響應(yīng),并返回合適的類型對(duì)象陋率。這個(gè)方法會(huì)在一個(gè)
* 工作線程中被調(diào)用(即不會(huì)在 UI 線程中調(diào)用此方法),如果此方法返回 null秽晚,則結(jié)果并不會(huì)被發(fā)送瓦糟。
*
* @param response 來自于網(wǎng)絡(luò)請(qǐng)求的響應(yīng)
* @return 解析的結(jié)果,如果發(fā)生錯(cuò)誤則返回 null
*/
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
/**
* 子類必須實(shí)現(xiàn)這個(gè)方法赴蝇,把解析的結(jié)果發(fā)送給監(jiān)聽器菩浙。其中 T 類型的參數(shù) response 要保證
* 不可以為 null,如果解析失敗句伶,則解析的結(jié)果不會(huì)通過此方法發(fā)送劲蜻。
*
* @param response 通過 {@link #parseNetworkResponse(NetworkResponse)} 方法解析的結(jié)果
*/
abstract protected void deliverResponse(T response);
- 默認(rèn)實(shí)現(xiàn)
Request
的子類有:StringRequest
、JsonObjectRequest
考余、
JsonArrayRequest
和ImageRequest
- 我們也可以自定義一個(gè)實(shí)現(xiàn)
Request
的類先嬉,實(shí)現(xiàn)上面兩個(gè)方法,將其加入到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中進(jìn)行網(wǎng)絡(luò)請(qǐng)求楚堤。在下一篇博客中將會(huì)舉兩個(gè)自定義Request
的例子 - Volley 中包括 8 種 Http 網(wǎng)絡(luò)請(qǐng)求方式:
GET
疫蔓、POST
含懊、PUT
、DELETE
衅胀、HEAD
岔乔、
OPTIONS
、TRACE
滚躯、PATCH
-
Request
類中包含了網(wǎng)絡(luò)請(qǐng)求的url
雏门,請(qǐng)求方式,請(qǐng)求Header
掸掏,請(qǐng)求Body
和請(qǐng)求的優(yōu)先級(jí)等信息剿配。 - 以下三個(gè)方法也經(jīng)常被子類重寫
/**
* 返回一個(gè) Map 類型的參數(shù),為這個(gè)請(qǐng)求添加網(wǎng)絡(luò)請(qǐng)求頭信息 Http Header阅束。
* 最常用的就是可以把 Cookie 信息通過此方法添加
*/
public Map<String, String> getHeaders() throws AuthFailureError {
return Collections.emptyMap();
}
/**
* 返回一個(gè)字節(jié)數(shù)組的對(duì)象作為 POST 或 PUT 請(qǐng)求的 Body 內(nèi)容呼胚。
*
* 當(dāng)重寫此方法時(shí),也需要考慮重寫 {@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()} 沒有被重寫的情況下息裸,可以通過此方法返回一個(gè)
* Map 類型的參數(shù)蝇更,用于構(gòu)建 POST 或 PUT 請(qǐng)求方式的 Body 內(nèi)容
*
* 注意:也可以通過直接重寫 {@link #getBody()} 方法自定義 Body 數(shù)據(jù)。
*/
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}
RequestQueue 源碼分析
RequestQueue
是 Volley 中的核心類呼盆,主要用于處理添加進(jìn)來的網(wǎng)絡(luò)請(qǐng)求年扩。
在本小節(jié)中將會(huì)分三部分介紹 RequestQueue
的類,分別是:RequestQueue
中的主要屬性访圃、RequestQueue
類的構(gòu)造方法和 RequestQueue
的主要方法厨幻。
RequestQueue 中的主要屬性
/**
* 維護(hù)了一個(gè)等待請(qǐng)求的集合,如果有一個(gè)請(qǐng)求正在被處理并且可以被緩存腿时,如果有新的相同
* URL 請(qǐng)求被添加進(jìn)來以后况脆,則會(huì)新的請(qǐng)求則會(huì)進(jìn)入此集合中。此集合主要是為了避免相同的
* 且不必要的網(wǎng)絡(luò)請(qǐng)求
*/
private final Map<String, Queue<Request<?>>> mWaitingRequests =
new HashMap<String, Queue<Request<?>>>();
/**
* 正在被此 RequestQueue 處理的請(qǐng)求的集合批糟,如果一個(gè)請(qǐng)求正在等待被處理或者正在被
* 某個(gè)調(diào)度線程處理格了,則它會(huì)在此集合中
*/
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
/** 緩存請(qǐng)求隊(duì)列,在此隊(duì)列中的請(qǐng)求徽鼎,將通過緩存獲取數(shù)據(jù) */
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
/** 網(wǎng)絡(luò)請(qǐng)求隊(duì)列盛末,在此隊(duì)列中的請(qǐng)求,將通過網(wǎng)絡(luò)向服務(wù)器發(fā)送請(qǐng)求獲取數(shù)據(jù) */
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
在 RequestQueue
中有兩個(gè) 基于優(yōu)先級(jí) Request
的隊(duì)列:mCacheQueue
緩存請(qǐng)求隊(duì)列和 mNetworkQueue
網(wǎng)絡(luò)請(qǐng)求隊(duì)列
RequestQueue 的構(gòu)造方法
/** 默認(rèn)的網(wǎng)絡(luò)請(qǐng)求調(diào)度線程數(shù)量 4 */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/** Cache 接口否淤,用戶獲取和緩存響應(yīng)結(jié)果悄但,默認(rèn)的實(shí)現(xiàn)類是 DiskBasedCache */
private final Cache mCache;
/** Network 接口,用于執(zhí)行網(wǎng)絡(luò)請(qǐng)求石抡,默認(rèn)的實(shí)現(xiàn)類是 BasicNetwork */
private final Network mNetwork;
/** 響應(yīng)分發(fā)器檐嚣,默認(rèn)的實(shí)現(xiàn)類是 ExecutorDelivery */
private final ResponseDelivery mDelivery;
/** 網(wǎng)絡(luò)調(diào)度線程數(shù)組,NetworkDispatcher是 {@link Thread} 的子類*/
private NetworkDispatcher[] mDispatchers;
/** 緩存調(diào)度線程汁雷,是{@link Thread} 的子類*/
private CacheDispatcher mCacheDispatcher;
/**
* 創(chuàng)建工作線程池净嘀,不調(diào)用 {@link #start()} 方法报咳,就不會(huì)開始開始工作,所以創(chuàng)建完請(qǐng)求隊(duì)列以后挖藏,必須調(diào)用{@link #start()}
*
* @param cache 向磁盤持久化響應(yīng)結(jié)果的緩存對(duì)象
* @param network 執(zhí)行 Http 請(qǐng)求的對(duì)象
* @param threadPoolSize 網(wǎng)絡(luò)請(qǐng)求調(diào)度線程的數(shù)量
* @param delivery 一個(gè)負(fù)責(zé)分發(fā)響應(yīng)結(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)} 實(shí)現(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)} 實(shí)現(xiàn)
*/
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
- 有三個(gè)構(gòu)造方法暑刃,最終調(diào)用的是
RequestQueue(Cache, Network, int,ResponseDelivery)
這個(gè)構(gòu)造方法 - 創(chuàng)建一個(gè)
ExecutorDelivery
對(duì)象并賦值給mDelivery
,其中在ExecutorDelivery
構(gòu)造函數(shù)中傳入的Handler
對(duì)象中的Looper
對(duì)象是主線程的膜眠,這樣使用mDelivery
發(fā)送的請(qǐng)求響應(yīng)結(jié)果或者異常就被發(fā)送到主線程中了 - 創(chuàng)建一個(gè)
NetworkDispatcher
類型的數(shù)組對(duì)象mDispatchers
岩臣,默認(rèn)長度是4 - 可以看到所依賴的屬性
mCache
、mNetwork
宵膨、mDelivery
都是接口類型的架谎,而不是具體的實(shí)現(xiàn)類,這充分體現(xiàn)了面向接口編程的思想
RequestQueue 中的主要方法
還記得在 Volley
類中的 newRequestQueue(Context, HttpStack)
創(chuàng)建完成 RequestQueue
對(duì)象 queue
以后辟躏,還調(diào)用的了 queue.start()
方法谷扣,start()
相關(guān)方法如下所示:
/**
* 啟動(dòng)在此隊(duì)列中的線程
*/
public void start() {
stop(); // 在啟動(dòng)之前,需要確鄙铀觯現(xiàn)在正在運(yùn)行 mCacheDispatcher 線程和 mDispatchers[] 中的線程被終止
// 創(chuàng)建緩存調(diào)度線程并啟動(dòng)
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// 根據(jù) mDispatchers[] 的長度会涎,創(chuàng)建對(duì)應(yīng)數(shù)量的網(wǎng)絡(luò)調(diào)度線程添加進(jìn) mDispatchers[] 并啟動(dòng)
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)絡(luò)調(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)建完該對(duì)象以后都需要進(jìn)行調(diào)用start()
方法啟動(dòng)該線程末秃。到這塊兒的代碼,意識(shí)到有必要看一下NetworkDispatcher
和CacheDispatcher
這兩個(gè)類的代碼了籽御,先不著急练慕,我們先分析完RequestQueue
的代碼。
通過 Volley 進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí)技掏,創(chuàng)建完網(wǎng)絡(luò)請(qǐng)求之后铃将,需要將網(wǎng)絡(luò)請(qǐng)求通過 RequestQueue.add(Request)
方法,將網(wǎng)絡(luò)請(qǐng)求添加進(jìn)網(wǎng)絡(luò)請(qǐng)求隊(duì)列零截,那么來分析下 add(Request)
方法麸塞,這個(gè)方法是 RequestQueue
中非常重要的一個(gè)方法。
/** 用于為請(qǐng)求生成一個(gè)自動(dòng)增長的序列號(hào) */
private AtomicInteger mSequenceGenerator = new AtomicInteger();
.....
/**
* 得到一個(gè)序列號(hào)
*/
public int getSequenceNumber() {
return mSequenceGenerator.incrementAndGet();
}
.....
/**
* 向請(qǐng)求隊(duì)列中添加一個(gè)請(qǐng)求
* @param request 向服務(wù)器發(fā)送的請(qǐng)求
* @return 已經(jīng)發(fā)送經(jīng)過處理的請(qǐ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.
// 為請(qǐng)求 request 設(shè)置請(qǐng)求隊(duì)列涧衙,并將其添加進(jìn) mCurrentRequests 隊(duì)列中
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// Process requests in the order they are added.
// 根據(jù)他們添加進(jìn)來的順序設(shè)置唯一的序列號(hào)
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// If the request is uncacheable, skip the cache queue and go straight to the network.
// 如果該請(qǐng)求 request 不可以緩存的,則跳過緩存隊(duì)列奥此,直接進(jìn)入網(wǎng)絡(luò)請(qǐng)求隊(duì)列
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(其實(shí)就是url)判斷 mWaitingRequests 中是否有相同的請(qǐng)求正在進(jìn)行弧哎,如果有,則將其添加進(jìn) mWaitingRequests 隊(duì)列中
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
// 如果有相同的請(qǐng)求稚虎,則進(jìn)行入隊(duì)操作
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 中沒有相同的請(qǐng)求正在進(jìn)行撤嫩,則在 mWaitingRequests 插入一個(gè) null 值
mWaitingRequests.put(cacheKey, null);
// 將該 request 添加進(jìn) mCacheQueue 隊(duì)列中
mCacheQueue.add(request);
}
return request;
}
}
- 通過
add(Request)
添加一個(gè)網(wǎng)絡(luò)請(qǐng)求,首先需要將該請(qǐng)求request
添加到mCurrentRequests
隊(duì)列中 - 如果該請(qǐng)求不走緩存蠢终,則直接將該請(qǐng)求
request
添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列mNetworkQueue
中序攘,結(jié)束該方法 - 如果該請(qǐng)求
request
可以走緩存茴她,根據(jù)cacheKey
(其實(shí)就是url
)判斷mWaitingRequests
中是否有相同的請(qǐng)求正在進(jìn)行,如果有程奠,則將其添加進(jìn)mWaitingRequests
隊(duì)列中丈牢,如果沒有則在mWaitingRequests
中添加值為null
的值,并將其添加進(jìn)緩存請(qǐng)求隊(duì)列mCacheQueue
中 -
RequestQueue.add(Request)
方法的流程圖(該圖出自 Volley 源碼解析)如下所示:
RequestQueue
還有一個(gè)常用的方法:RequestQueue.cancelAll(Object)
/**
* 一個(gè)在 {@link RequestQueue#cancelAll(RequestFilter)} 方法中使用的判斷或過濾接口
*/
public interface RequestFilter {
public boolean apply(Request<?> request);
}
/**
* 將此隊(duì)列中符合 filter 條件的所有請(qǐng)求取消
* @param filter 使用的過濾條件
*/
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}
/**
* 通過給定的 tag 取消在此隊(duì)列中所有 tag 相同的請(qǐng)求瞄沙,tag絕對(duì)不可以為 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;
}
});
}
- 一般我都會(huì)重寫每個(gè)請(qǐng)求對(duì)象
***Request
的setTag()
方法返回一個(gè)該請(qǐng)求的TAG
己沛,在合適的地方(比如:Activity.onDestory()
方法中)通過cancelAll(Object)
取消該TAG
對(duì)應(yīng)的請(qǐng)求,以防止發(fā)生意外(比如:內(nèi)存泄露)距境。
NetworkDispatcher 源碼分析
由于 NetworkDispatcher
源碼也并不算長申尼,只有100+行,直接上源碼垫桂,里面的注釋也很詳細(xì)了师幕,后面會(huì)配有相應(yīng)的說明和流程圖。
public class NetworkDispatcher extends Thread {
/** 請(qǐng)求服務(wù)器的網(wǎng)絡(luò)請(qǐng)求隊(duì)列 */
private final BlockingQueue<Request<?>> mQueue;
/** 處理網(wǎng)絡(luò)請(qǐng)求的實(shí)現(xiàn) Network 接口類的對(duì)象 */
private final Network mNetwork;
/** 寫緩存的對(duì)象 */
private final Cache mCache;
/** 用于發(fā)送響應(yīng)和異常的分發(fā)器 */
private final ResponseDelivery mDelivery;
/** 用于標(biāo)志此線程是否中斷 Used for telling us to die. */
private volatile boolean mQuit = false;
/**
* 創(chuàng)建一個(gè)網(wǎng)絡(luò)調(diào)度線程诬滩,必須調(diào)用 {@link #start()} 啟動(dòng)此線程
*
* @param queue 網(wǎng)絡(luò)請(qǐng)求隊(duì)列
* @param network 執(zhí)行網(wǎng)絡(luò)請(qǐng)求的 Network 接口實(shí)現(xiàn)類
* @param cache 將響應(yīng)寫進(jìn)緩存的 Cache 接口實(shí)現(xiàn)類
* @param delivery 用于分發(fā)請(qǐng)求結(jié)果的分發(fā)器
*/
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
/**
* 強(qiáng)制此調(diào)度線程立即停止霹粥。如果在隊(duì)列中仍然有請(qǐng)求,它們不能保證一定會(huì)被處理
*/
public void quit() {
mQuit = true;
interrupt();
}
......
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
// 記錄網(wǎng)絡(luò)請(qǐng)求開始的時(shí)間
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// Take a request from the queue.
// 從隊(duì)列中取出一個(gè)網(wǎng)絡(luò)請(qǐ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.
// 如果該請(qǐng)求已經(jīng)被取消碱呼,則不會(huì)進(jìn)行網(wǎng)絡(luò)請(qǐng)求
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// Perform the network request.
// 執(zhí)行網(wǎng)絡(luò)請(qǐ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.
// 如果服務(wù)器返回的 304蒙挑,并且該請(qǐng)求之前已經(jīng)得到過并發(fā)送過響應(yīng)結(jié)果,則響應(yīng)結(jié)果可以復(fù)用愚臀,沒必要進(jìn)行新的網(wǎng)絡(luò)請(qǐng)求忆蚀,結(jié)束本次循環(huán)
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
// 在工作線程中解析得到的網(wǎng)絡(luò)請(qǐng)求響應(yī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()} 方法判斷此請(qǐng)求是否需要進(jìn)行緩存,如果需要進(jìn)行緩存處理姑裂,則將其放到 mCache 中
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back
// 將響應(yīng)通過 mDelivery 發(fā)送到 UI 線程中
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
// 如果拋出 VolleyError 異常馋袜,則將網(wǎng)絡(luò)請(qǐng)求耗時(shí)通過{@link volleyError#setNetworkTimeMs(long)}
// 放進(jìn) volleyError 中,并通過 mDelivery 將異常發(fā)送出去
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
}
catch (Exception e) {
// 若發(fā)生其他異常舶斧,則生成 VolleyError 對(duì)象,并將網(wǎng)絡(luò)請(qǐng)求耗時(shí)放進(jìn) volleyError 對(duì)象中茴厉,
// 并通過 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()
方法中有一個(gè)while(true){ ...... }
代碼段泽台,可見啟動(dòng)此線程以后,它就進(jìn)入一直在循環(huán)的狀態(tài)矾缓,不斷的從網(wǎng)絡(luò)請(qǐng)求隊(duì)列mQueue
中取出網(wǎng)絡(luò)請(qǐng)求并執(zhí)行怀酷,除非通過quit()
方法改變mQuit
成為true
,該方法才會(huì)停止 -
NetworkDispatcher
類中所依賴的屬性嗜闻,mNetwork
蜕依、mCache
和mDelivery
都是接口類型的,而不是具體的實(shí)現(xiàn)類,這也充分體現(xiàn)了面向接口編程的思想 - 在上面的代碼片段中样眠,注釋已經(jīng)很清楚了友瘤。下面是一張
NetworkDispatcher
進(jìn)行網(wǎng)絡(luò)請(qǐng)求的流程圖,出自 Volley 源碼解析檐束。
CacheDispatcher 源碼分析
public class CacheDispatcher extends Thread {
private static final boolean DEBUG = VolleyLog.DEBUG;
/** 緩存請(qǐng)求隊(duì)列 The queue of requests coming in for triage. */
private final BlockingQueue<Request<?>> mCacheQueue;
/** 網(wǎng)絡(luò)請(qǐng)求隊(duì)列 The queue of requests going out to the network. */
private final BlockingQueue<Request<?>> mNetworkQueue;
/** 緩存接口 The cache to read from. */
private final Cache mCache;
/** 請(qǐng)求結(jié)果分發(fā)類 For posting responses. */
private final ResponseDelivery mDelivery;
/** 用于標(biāo)志此線程是否中斷 Used for telling us to die. */
private volatile boolean mQuit = false;
/**
* 創(chuàng)建一個(gè) 緩存調(diào)度線程辫秧,必須調(diào)用 {@link #start()} 方法,此線程才會(huì)開始工作
*
* @param cacheQueue 緩存請(qǐng)求隊(duì)列
* @param networkQueue 網(wǎng)絡(luò)請(qǐng)求隊(duì)列
* @param cache 處理緩存的對(duì)象
* @param delivery 分發(fā)響應(yīng)的結(jié)果
*/
public CacheDispatcher(
BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}
/**
* 強(qiáng)制此線程立即停止厢塘,如果在隊(duì)列中有請(qǐng)求茶没,則不能保證請(qǐng)求一定會(huì)被處理
*/
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.
// 從緩存隊(duì)列中取一個(gè)請(qǐng)求,如果沒有可用的則阻塞
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
// 如果緩存請(qǐng)求已經(jīng)被取消晚碾,則不用處理它
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
// 通過 request.getCacheKey() 從緩存中取出對(duì)應(yīng)的緩存記錄
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
// 如果沒有得到緩存結(jié)果抓半,則將該請(qǐng)求加入到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中
mNetworkQueue.put(request);
continue;
}
// If it is completely expired, just send it to the network.
// 如果緩存已經(jīng)過期,則將該請(qǐng)求添加進(jìn)網(wǎng)絡(luò)請(qǐng)求隊(duì)列中
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é)果格嘁,則生成對(duì)應(yīng)的響應(yīng)
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.
// 還存在這樣一種情況,緩存記錄存在糕簿,但是它約定的生存時(shí)間已經(jīng)到了(還未完全過期探入,
// 叫軟過期),可以將其發(fā)送到主線程去更新
// 但同時(shí)懂诗,也要從網(wǎng)絡(luò)中更新它的數(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.
// 將其傳回主線程的同時(shí)蜂嗽,將請(qǐng)求放到Network隊(duì)列中
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)建該類的對(duì)象以后都需要通過start()
方法啟動(dòng)它 -
CacheDispatcher
所依賴的對(duì)象mCache
和mDelivery
都是接口類型的植旧,而不是具體的實(shí)現(xiàn)類,這也是面向接口編程思想的體現(xiàn) - 下面是一張
CacheDispatcher
進(jìn)行緩存請(qǐng)求的流程圖离唐,同樣出自 Volley 源碼解析病附。
BasicNetwork 源碼分析
BasicNetwork
是 Network
接口的實(shí)現(xiàn)類,Network
接口是執(zhí)行網(wǎng)絡(luò)請(qǐng)求的接口亥鬓,其中只有一個(gè)方法 performRequest(Request)
完沪,該方法由于執(zhí)行網(wǎng)絡(luò)請(qǐng)求并返回 NetworkResponse
類型的請(qǐng)求結(jié)果,那來看一下在 BasicNetwork
中該方法是怎么實(shí)現(xiàn)的
public class BasicNetwork implements Network {
protected final HttpStack mHttpStack;
......
/**
* 通過 {@link HttpStack#performRequest(Request, Map)} 方法執(zhí)行網(wǎng)絡(luò)請(qǐng)求嵌戈,將得到的網(wǎng)絡(luò)請(qǐng)求響應(yīng)包裝成 NetworkResponse 類型的對(duì)象并將其返回
*/
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
// 記錄請(qǐng)求開始的時(shí)間
long requestStart = SystemClock.elapsedRealtime();
while (true) {
// 請(qǐng)求響應(yīng)的對(duì)象引用
HttpResponse httpResponse = null;
// 請(qǐng)求響應(yīng)中的 body
byte[] responseContents = null;
// 請(qǐng)求響應(yīng)中的 header
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// 請(qǐng)求頭
Map<String, String> headers = new HashMap<String, String>();
// 將請(qǐng)求中添加的 CacheEntry 添加到請(qǐng)求頭中覆积,詳見 {@link addCacheHeaders(Map<String, String>, Cache.Entry)} 方法
addCacheHeaders(headers, request.getCacheEntry());
// 通過 mHttpStack.performRequest(Request, Map<String, String>) 方法執(zhí)行具體的網(wǎng)絡(luò)請(qǐng)求,并得到請(qǐng)求的響應(yīng)
httpResponse = mHttpStack.performRequest(request, headers);
// 請(qǐng)求響應(yīng)中的狀態(tài)行信息對(duì)象
StatusLine statusLine = httpResponse.getStatusLine();
// 請(qǐng)求響應(yīng)中的 狀態(tài)碼
int statusCode = statusLine.getStatusCode();
// 響應(yīng)中的響應(yīng)頭 Header[] 轉(zhuǎn)換成 Map<String, String> 的形式
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
// 如果響應(yīng)中狀態(tài)碼是 304熟呛,則表示請(qǐng)求的內(nèi)容在服務(wù)器端沒有更改技健,使用本地的緩存即可
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
// 取出緩存 entry 對(duì)象
Entry entry = request.getCacheEntry();
if (entry == null) {
// 如果 entry 對(duì)象為 null,則返回一個(gè) entry 為 null 的 NetworkResponse 對(duì)象
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 的響應(yīng)惰拱,但是真正的響應(yīng)中的響應(yīng)頭包括兩部分信息:當(dāng)前返回的響應(yīng)頭和緩存中已經(jīng)緩存的響應(yīng)頭
entry.responseHeaders.putAll(responseHeaders);
// 使用緩存對(duì)象 entry 生成一個(gè) NetworkResponse 對(duì)象并返回
// 將請(qǐng)求耗時(shí)放入 NetworkResponse 對(duì)象中
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.
// 有一些響應(yīng)(比如:204)并沒有 body 內(nèi)容,所以必須進(jìn)行檢查
if (httpResponse.getEntity() != null) {
// 將響應(yīng)中的 HttpEntity 對(duì)象轉(zhuǎn)換成 byte[] 類型的 responseContents 對(duì)象,詳見 {@link entityToBytes(HttpEntity)} 方法
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
// 如果響應(yīng)中的 HttpEntity 為空偿短,也要給 responseContents 對(duì)象賦值
responseContents = new byte[0];
}
// if the request is slow, log it.
// 如果請(qǐng)求的耗時(shí)太久則打印請(qǐng)求相關(guān)的信息欣孤,詳見 {@link logSlowRequests(long, Request ,byte[], StatusLine)} 方法
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
// 如果響應(yīng)狀態(tài)碼超出 200-299 的范圍,則拋出 IOException 異常
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
// 如果響應(yīng)狀態(tài)碼在 200-299 之內(nèi)昔逗,則生成 NetworkResponse 對(duì)象并返回
// 將請(qǐng)求耗時(shí)放入 NetworkResponse 對(duì)象中
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
// 處理 SocketTimeout降传,重復(fù)請(qǐng)求
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
// 處理 ConnectTimeout,重復(fù)請(qǐng)求
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
// 處理 MalformedURLException勾怒,重復(fù)請(qǐng)求
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
// 處理 IOException婆排,重復(fù)請(qǐng)求
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
類中最重要的一個(gè)屬性就是mHttpStack
是HttpStack
類型的,HttpStack
也是接口類型的笔链,它是具體的執(zhí)行網(wǎng)絡(luò)請(qǐng)求的接口段只,在 Volley 中有兩個(gè)實(shí)現(xiàn)了HttpStack
接口的類:HurlStack
和HttpClientStack
,HurlStack
內(nèi)部是使用HttpUrlConnection
實(shí)現(xiàn)的鉴扫,而HttpClientStack
內(nèi)部是使用HttpClient
實(shí)現(xiàn)的 - 在
BasicNetwork
中赞枕,對(duì)響應(yīng)狀態(tài)碼為304
、204
等特殊情況做了一定的處理坪创,如果狀態(tài)碼在200-299
之外則拋出IOException
炕婶,在200-299
之內(nèi)則生成NetworkResponse
對(duì)象并返回,并對(duì)各種異常SocketTimeoutException
和ConnectTimeoutException
等異常做了特殊的處理 - 在
performRequest(Request)
中起始的位置記錄請(qǐng)求開始的時(shí)間莱预,在生成NetworkResponse
對(duì)象或者拋出VolleyError
異常中都代碼網(wǎng)絡(luò)請(qǐng)求的時(shí)間柠掂,這是第一篇博客中請(qǐng)求耗時(shí)的原始值
ExecutorDelivery 源碼分析
ExecutorDelivery
是 ResponseDelivery
接口的實(shí)現(xiàn)類,ResponseDelivery
接口主要有三個(gè)方法:
public interface ResponseDelivery {
/**
* 解析一個(gè)來自網(wǎng)絡(luò)或者緩存的響應(yīng)結(jié)果并發(fā)送
*/
public void postResponse(Request<?> request, Response<?> response);
/**
* 解析一個(gè)來自網(wǎng)絡(luò)或者緩存的響應(yīng)結(jié)果并發(fā)送依沮,提供的 Runnable 對(duì)象會(huì)在發(fā)送完結(jié)果之后被執(zhí)行
*/
public void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/**
* 向該 request 對(duì)象發(fā)送一個(gè)異常
*/
public void postError(Request<?> request, VolleyError error);
}
接著看一下 ExecutorDelivery
實(shí)現(xiàn)類的代碼
public class ExecutorDelivery implements ResponseDelivery {
/** 向主線程中發(fā)送結(jié)果的線程池對(duì)象 */
private final Executor mResponsePoster;
/**
* ExecutorDelivery 的構(gòu)造方法
* @param handler {@link Handler} 對(duì)象決定了是向哪個(gè)線程發(fā)送結(jié)果
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
// 使用提供的 handler 對(duì)象實(shí)現(xiàn)一個(gè) Executor 對(duì)象
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ù)傳進(jìn)來的 request 涯贞、response 和 runnable 對(duì)象,生成一個(gè) ResponseDeliveryRunnable 對(duì)象悉抵,并使用 mResponsePoster 線程池運(yùn)行
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));
}
/**
* 一個(gè)用于將網(wǎng)絡(luò)請(qǐng)求的響應(yīng)結(jié)果發(fā)送到主線程的線程對(duì)象
*/
@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.
// 如果請(qǐng)求已經(jīng)被取消肩狂,則結(jié)束它并不會(huì)發(fā)送該請(qǐng)求的結(jié)果
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
// 視情況通過不同的方法發(fā)送正確的響應(yīng)或異常
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.
// 添加標(biāo)志
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
// 如果 mRunnable 對(duì)象不為 null, 則執(zhí)行它
if (mRunnable != null) {
mRunnable.run();
}
}
}
}
- 在
ExecutorDelivery
的構(gòu)造方法中,需要傳入一個(gè)Handler
對(duì)象姥饰,這個(gè)Handler
對(duì)象是非常重要的傻谁。都知道每個(gè)Handler
對(duì)象都會(huì)持有一個(gè)Looper
對(duì)象,該對(duì)象決定了Handler
發(fā)送的消息或者任務(wù)在那個(gè)線程中處理列粪。在分析RequestQueue
源碼時(shí)审磁,是這樣new ExecutorDelivery(new Handler(Looper.getMainLooper()))
生成的ExecutorDelivery
默認(rèn)的對(duì)象,所以默認(rèn)情況下岂座,通過ExecutorDelivery
對(duì)象發(fā)送的消息都是在主線程中處理的态蒂。這也符合我們的習(xí)慣,具體的網(wǎng)絡(luò)請(qǐng)求結(jié)果都是在 UI 線程中直接處理费什,這樣更方便一些 - 在
ExecutorDelivery
中的內(nèi)部類ResponseDeliveryRunnable
钾恢,是非常重要的,在它的run()
方法中,如果成功則調(diào)用request
的deliverResponse(T)
方法,否則調(diào)用deliverError(VolleyError)
方法。這里的deliverResponse(T)
方法內(nèi)部最終會(huì)回調(diào)我們?cè)跇?gòu)建Request
時(shí)設(shè)置的Response.Listener
對(duì)象onResponse(T)
方法的涤姊。
至此,關(guān)于Volley 源碼解析及對(duì) Volley 的擴(kuò)展
系列的第二篇文章就結(jié)束了崩哩,從這邊文章中也可以知道為什么都說 Volley
具有很強(qiáng)的擴(kuò)展性,因?yàn)楹芏嗟胤揭蕾嚨膶傩远际墙涌谘糟澹皇蔷唧w的實(shí)現(xiàn)類邓嘹。接下來在第三篇文章中就會(huì)對(duì) Volley
做一些擴(kuò)展。如果有什么問題歡迎指出险胰。我的工作郵箱:jiankunli24@gmail.com
參考資料:
Volley學(xué)習(xí)筆記之簡單使用及部分源碼詳解 -- Yongyu