從源碼分析Android的Volley庫(kù)的工作流程
Volley現(xiàn)在已經(jīng)被官方放到AOSP里面,已經(jīng)逐步成為Android官方推薦的網(wǎng)絡(luò)框架。
類抽象
對(duì)Http協(xié)議的抽象
Requeset
顧名思義,對(duì)請(qǐng)求的封裝,實(shí)現(xiàn)了Comparable接口,因?yàn)樵赩olley中是可以指定請(qǐng)求的優(yōu)先級(jí)的,實(shí)現(xiàn)Comparable是為了在Request任務(wù)隊(duì)列中進(jìn)行排序,優(yōu)先級(jí)高的Request會(huì)被優(yōu)先調(diào)度執(zhí)行。
NetworkResponse
Http響應(yīng)的封裝,其中包括返回的狀態(tài)碼 頭部 數(shù)據(jù)等。
Response
給調(diào)用者返回的結(jié)果封裝,它比NetworkResponse更加簡(jiǎn)單,只包含三個(gè)東西:數(shù)據(jù) 異常 和 Cache數(shù)據(jù)。
Network
對(duì)HttpClient的抽象,接受一個(gè)Request,返回一個(gè)NetworkResponse
反序列化抽象
所謂反序列化,就是將網(wǎng)絡(luò)中傳輸?shù)膶?duì)象變成一個(gè)Java對(duì)象,Volley中是通過(guò)擴(kuò)展Request類來(lái)實(shí)現(xiàn)不同的反序列化功能,如JsonRequest StringRequest,我們也可以通過(guò)自己擴(kuò)展一些Request子類,來(lái)實(shí)現(xiàn)對(duì)請(qǐng)求流的各種定制。
請(qǐng)求工作流抽象
RequestQueue
用來(lái)管理各種請(qǐng)求隊(duì)列,其中包含有4個(gè)隊(duì)列
a) 所有請(qǐng)求集合,通過(guò)RequestQueue.add()添加的Request都會(huì)被添加進(jìn)來(lái),當(dāng)請(qǐng)求結(jié)束之后刪除。
b) 所有等待Request,這是Volley做的一點(diǎn)優(yōu)化,想象一下,我們同時(shí)發(fā)出了三個(gè)一模一樣的Request,此時(shí)底層其實(shí)不必真正走三個(gè)網(wǎng)絡(luò)請(qǐng)求,而只需要走一個(gè)請(qǐng)求即可。所以Request1被add之后會(huì)被調(diào)度執(zhí)行,而Request2 和Request3被加進(jìn)來(lái)時(shí),如果Request1還未執(zhí)行完畢,那么Request2和 Request3只需要等著Request1的結(jié)果即可。
c) 緩存隊(duì)列,其中的Request需要執(zhí)行查找緩存的工作
d) 網(wǎng)絡(luò)工作隊(duì)列 其中的Request需要被執(zhí)行網(wǎng)絡(luò)請(qǐng)求的工作
NetworkDispatcher
執(zhí)行網(wǎng)絡(luò)Request的線程,它會(huì)從網(wǎng)絡(luò)工作隊(duì)列中取出一個(gè)請(qǐng)求,并執(zhí)行。Volley默認(rèn)有四個(gè)線程作為執(zhí)行網(wǎng)絡(luò)請(qǐng)求的線程。
CacheDispatcher
執(zhí)行Cache查找的線程,它會(huì)從緩存隊(duì)列中取出一個(gè)請(qǐng)求,然后查找該請(qǐng)求的本地緩存。Volley只有一個(gè)線程執(zhí)行Cache任務(wù)。
ResponseDelivery
請(qǐng)求數(shù)據(jù)分發(fā)器,可以發(fā)布Request執(zhí)行的結(jié)果。
Cache
對(duì)Cache的封裝,主要定義了如何存儲(chǔ),獲取緩存,存取依據(jù)Request中的getCacheKey()來(lái)描述。
提交請(qǐng)求
Volley通過(guò)RequestQueue.add(Request)來(lái)往任務(wù)隊(duì)列中增加請(qǐng)求:

一個(gè)Request被提交之后有幾個(gè)去處:
1.Set<Request<?>> mCurrentRequests對(duì)應(yīng)所有請(qǐng)求隊(duì)列。所有調(diào)用add的Request必然都會(huì)添加到這里面來(lái)。
2.PriorityBlockingQueue<Request<?>> mNetworkQueue 對(duì)應(yīng)網(wǎng)絡(luò)隊(duì)列。如果一個(gè)Request不需要緩存,那么add之后會(huì)被直接添加到網(wǎng)絡(luò)隊(duì)列中。
3.PriorityBlockingQueue<Request<?>> mCacheQueue對(duì)應(yīng)緩存請(qǐng)求。如果一個(gè)Request需要緩存,并且當(dāng)前的RequestQueue中并沒(méi)有一個(gè)Request的getCacheKey和當(dāng)前Request相同(可以認(rèn)為一個(gè)請(qǐng)求),那么加入緩存隊(duì)列,讓緩存工作線程來(lái)處理。
4.Map<String, Queue<Request<?>>> mWaitingRequests對(duì)應(yīng)等待隊(duì)列。如果RequestQueue中已經(jīng)有一個(gè)相同請(qǐng)求在處理,這里只需要將這個(gè)Request放到等待隊(duì)列中,等之前的Request結(jié)果回來(lái)之后,進(jìn)行處理即可。
Volley提交任務(wù)到隊(duì)列中是不是很簡(jiǎn)單?下面來(lái)說(shuō)說(shuō)優(yōu)先級(jí)請(qǐng)求的事情吧,你可能已經(jīng)注意到了,上面兩個(gè)存放需要執(zhí)行任務(wù)的隊(duì)列都是PriorityBlockingQueue,前面說(shuō)了Request現(xiàn)實(shí)了Comparable,看看這個(gè)方法:
@Override
public int compareTo(Request<T> other) {
Priority left = this.getPriority();
Priority right = other.getPriority();
//mSequence表示請(qǐng)求序列號(hào),add時(shí),會(huì)通過(guò)一個(gè)計(jì)數(shù)器來(lái)指定
return left == right ?
this.mSequence - other.mSequence :
right.ordinal() - left.ordinal();
}
所以,如果我們的工作線程(NetworkDispatcher,CacheDispatcher)取任務(wù)時(shí),自然會(huì)從頭部開(kāi)始取。
這里的優(yōu)先級(jí),僅僅是保證一個(gè)請(qǐng)求比另外一個(gè)請(qǐng)求先處理,而并不能保證一個(gè)高優(yōu)先級(jí)請(qǐng)求一定會(huì)比低優(yōu)先級(jí)的請(qǐng)求先回來(lái)
緩存工作線程處理
@Override
public void run() {
//初始化Cache
mCache.initialize();
Request<?> request;
while (true) {
//阻塞 獲取一個(gè)Cache任務(wù)
request = mCacheQueue.take();
try {
//已經(jīng)被取消
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
//如果拿cache未果,放入網(wǎng)絡(luò)請(qǐng)求隊(duì)列
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
mNetworkQueue.put(request);
continue;
}
//緩存超時(shí),放入網(wǎng)絡(luò)請(qǐng)求隊(duì)列
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
//根據(jù)Cache構(gòu)造Response
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
//是否超過(guò)軟過(guò)期
if (!entry.refreshNeeded()) {
// 直接返回Cache
mDelivery.postResponse(request, response);
} else {
request.setCacheEntry(entry);
//設(shè)置中間結(jié)果
response.intermediate = true;
//發(fā)送中間結(jié)果
final Request<?> finalRequest = request;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
//中間結(jié)果完事之后,講請(qǐng)求放入網(wǎng)絡(luò)隊(duì)列
mNetworkQueue.put(finalRequest);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (Exception e) {
}
}
}
這里可以看到Volley確實(shí)對(duì)緩存封裝很到位,各種情況都考慮到了,其中比較重要的兩點(diǎn):
取出來(lái)的Cache并不僅僅是數(shù)據(jù),同時(shí)還包括這次請(qǐng)求的一些Header
硬過(guò)期 軟過(guò)期
我們可以看到Cache中有兩個(gè)字段來(lái)描述緩存過(guò)期: Cache.ttl vs Cache.softTtl。什么區(qū)別呢?如果ttl過(guò)期,那么這個(gè)緩存永遠(yuǎn)不會(huì)被使用了;如果softTtl沒(méi)有過(guò)期,這個(gè)數(shù)據(jù)直接返回;如果softTtl過(guò)期,那么這次請(qǐng)求將有兩次返回,第一次返回這個(gè)Cahce,第二次返回網(wǎng)絡(luò)請(qǐng)求的結(jié)果。想想,這個(gè)是不是滿足我們很多場(chǎng)景呢?先進(jìn)入頁(yè)面展示緩存,然后再刷新頁(yè)面;如果這個(gè)緩存太久了,可以等待網(wǎng)絡(luò)數(shù)據(jù)回來(lái)之后再展示數(shù)據(jù),是不是很贊?
NetworkDispatcher
執(zhí)行網(wǎng)絡(luò)請(qǐng)求的工作線程,默認(rèn)有4個(gè)線程,它不停地從網(wǎng)絡(luò)隊(duì)列中取任務(wù)執(zhí)行。
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
// release previous request object to avoid leaking request object when mQueue is drained.
request = null;
try {
request = mQueue.take();
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
//取消
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
//通過(guò)Http棧實(shí)現(xiàn)客戶端發(fā)送網(wǎng)絡(luò)請(qǐng)求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果緩存軟過(guò)期,那么會(huì)重新走網(wǎng)絡(luò);如果server返回304,表示上次之后請(qǐng)求結(jié)果數(shù)據(jù)本地并沒(méi)有過(guò)期,所以可以直接用本地的,因?yàn)橹癡olley已經(jīng)發(fā)過(guò)一次Response了,所以這里就不需要再發(fā)送Response結(jié)果了。
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
//更新緩存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
//發(fā)送結(jié)果
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
Request
Request中主要封裝了一個(gè)請(qǐng)求的各類Http協(xié)議信息,比如 URL,請(qǐng)求方法,請(qǐng)求的優(yōu)先級(jí),請(qǐng)求重試的策略,緩存策略等。
這里說(shuō)一下其中比較有意思的重發(fā)策略,如果一次請(qǐng)求發(fā)生超時(shí)異常,比如SocketTimeoutException ConnectTimeoutException ,我們可以為Request配置一個(gè)RetryPolicy,你可以指定重發(fā)這個(gè)Request的次數(shù),以及每次失敗之后重新設(shè)置這個(gè)請(qǐng)求的超時(shí)時(shí)間(第一次失敗之后,你可以調(diào)整第二次請(qǐng)求的超時(shí)時(shí)間增加,以減少失敗的可能性)。
反序列化
Request最重要的功能就是提供了內(nèi)容的反序列化,通過(guò)不同的子類來(lái)實(shí)現(xiàn)不同的序列化功能。比如,如果請(qǐng)求結(jié)果是一個(gè)Json的對(duì)象,我們可以使用JsonObjectRequest,如果是一個(gè)普通字符,使用StringRequest,同時(shí),我們也可以很方便的定制自己的Request,通過(guò)復(fù)寫Response<T> parseNetworkResponse(NetworkResponse response);方法即可。
默認(rèn)的JsonRequest使用org.json中的Json解析,我們使用Gson來(lái)進(jìn)行解析能夠構(gòu)造一個(gè)更加通用的處理json返回的Request:
public class JsonGRequest<T> extends Request<T> {
private static Gson gson = new Gson();
private Response.Listener<T> mListener;
public JsonGRequest(String url, Response.ErrorListener listener,Response.Listener responseListener) {
super(url, listener);
this.mListener = mListener;
}
public JsonGRequest(int method, String url, Response.ErrorListener listener) {
super(method, url, listener);
}
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
return Response.success(gson.fromJson(new InputStreamReader(new ByteArrayInputStream(response.data)),getType()), HttpHeaderParser.parseCacheHeaders(response))
}
@Override
protected void deliverResponse(T response) {
if(mListener != null) {
mListener.onResponse(response);
}
}
//獲取指定的泛型類型
protected Type getType() {
Type superclass;
for(superclass = this.getClass().getGenericSuperclass(); superclass instanceof Class && !superclass.equals(JsonGRequest.class); superclass = ((Class)superclass).getGenericSuperclass()) {
;
}
if(superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
} else {
ParameterizedType parameterized = (ParameterizedType)superclass;
return parameterized.getActualTypeArguments()[0];
}
}
}
ImageRequest
Volley專門為圖片請(qǐng)求提供了ImageRequest,主要是反序列化了一下數(shù)據(jù)流到BitMap,還可以制定圖片的大小,質(zhì)量等參數(shù)。
ImageLoader是Volley提供的一個(gè)用來(lái)加載圖片的工具,它的內(nèi)部還是使用ImageRequest來(lái)實(shí)現(xiàn)的,主要新加的功能是增加了內(nèi)存緩存,你可以通過(guò)配置ImageCache來(lái)設(shè)置內(nèi)存緩存。
相關(guān)文章
Kotlin + Flow 實(shí)現(xiàn)Android 應(yīng)用初始化任務(wù)啟動(dòng)庫(kù)
這篇文章主要介紹了Kotlin + Flow 實(shí)現(xiàn)Android 應(yīng)用初始化任務(wù)啟動(dòng)庫(kù)的方法,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03
詳解Android PopupWindow怎么合理控制彈出位置(showAtLocation)
本篇文章主要介紹了詳解Android PopupWindow怎么合理控制彈出位置(showAtLocation),具有一定的參考價(jià)值,有興趣的可以了解一下2017-10-10
Android實(shí)現(xiàn)鍵盤彈出界面上移的實(shí)現(xiàn)思路
這篇文章主要介紹了Android實(shí)現(xiàn)鍵盤彈出界面上移的實(shí)現(xiàn)思路,需要的朋友可以參考下2018-04-04
Android創(chuàng)建Menu菜單實(shí)例
這篇文章主要介紹了Android創(chuàng)建Menu菜單實(shí)例,講述了Android菜單項(xiàng)的創(chuàng)建方法,在Android應(yīng)用程序開(kāi)發(fā)中非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10
Android LayerDrawable超詳細(xì)講解
一個(gè)LayerDrawable是一個(gè)可以管理一組drawable對(duì)象的drawable。在LayerDrawable的drawable資源按照列表的順序繪制,所以列表的最后一個(gè)drawable繪制在最上層2022-11-11
Android ListView中headerview的動(dòng)態(tài)顯示和隱藏的實(shí)現(xiàn)方法
這篇文章主要介紹了Android ListView中headerview的動(dòng)態(tài)顯示和隱藏的實(shí)現(xiàn)方法的相關(guān)資料,這里提供兩種方法幫助實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-08-08

