簡(jiǎn)述
Volley是一個(gè)請(qǐng)求封裝框架,之所以說(shuō)是框架,是因?yàn)槠渲饕瞧鸬揭粋€(gè)分工拆分的作用,然后需要根據(jù)自身的使用情況來(lái)進(jìn)行各個(gè)功能組件的實(shí)現(xiàn),從而完成一套完整的網(wǎng)絡(luò)請(qǐng)求流程
接下來(lái)主要分析幾個(gè)主要的組件
基于Volley1.0.19版本
NetworkDispatcher
網(wǎng)絡(luò)調(diào)度者,顧名思義,實(shí)際上就是請(qǐng)求調(diào)度的作用,這個(gè)在Volley中是默認(rèn)實(shí)現(xiàn),接下來(lái)看一下Volley的請(qǐng)求調(diào)度流程
public class NetworkDispatcher extends Thread {
/** 當(dāng)前請(qǐng)求任務(wù)的隊(duì)列 */
private final BlockingQueue<Request<?>> mQueue;
/** 用于實(shí)際發(fā)起網(wǎng)絡(luò)請(qǐng)求的對(duì)象 */
private final Network mNetwork;
/** 用于寫(xiě)入請(qǐng)求結(jié)果的緩存 */
private final Cache mCache;
/** 用于進(jìn)行請(qǐng)求結(jié)果的分發(fā) */
private final ResponseDelivery mDelivery;
/** 當(dāng)前調(diào)度線程是否退出,一旦退出,當(dāng)前線程就會(huì)中斷 */
private volatile boolean mQuit = false;
//...
@Override
public void run() {
//設(shè)置線程優(yōu)先級(jí),在Android中這個(gè)優(yōu)先級(jí)相當(dāng)于中等,一般為后臺(tái)線程使用
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {//一個(gè)一直運(yùn)行的線程
long startTimeMs = SystemClock.elapsedRealtime();
// release previous request object to avoid leaking request object when mQueue is drained.
request = null;
try {
// 這里通過(guò)使用阻塞隊(duì)列,可以做到空閑的時(shí)候阻塞線程
// volley默認(rèn)使用的是PriorityBlockingQueue,在take的時(shí)候會(huì)把線程進(jìn)行await
// 那么下一次有數(shù)據(jù)進(jìn)入隊(duì)列的時(shí)候會(huì)notify再進(jìn)行喚醒
// 這樣可以很有效的避免線程的輪詢,也可以節(jié)省CPU的開(kāi)銷(xiāo)
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");
// 在請(qǐng)求正式開(kāi)始前首先要看一下當(dāng)前請(qǐng)求是否取消
// 如果取消的話沒(méi)有必要浪費(fèi)網(wǎng)絡(luò)鏈接的流量
// 注意這里取消后是不會(huì)進(jìn)行Listener的回調(diào)的
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
//通過(guò)預(yù)設(shè)的實(shí)際網(wǎng)絡(luò)請(qǐng)求執(zhí)行者進(jìn)行請(qǐng)求
//這里通過(guò)接口設(shè)計(jì),具有更好的擴(kuò)展性
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
//到這里結(jié)束后,實(shí)際上一次發(fā)往服務(wù)器的請(qǐng)求和接收服務(wù)器響應(yīng)的過(guò)程已經(jīng)完成
//一個(gè)請(qǐng)求完成了,這時(shí)候可能有兩種情況
//1.當(dāng)前請(qǐng)求在發(fā)送之前標(biāo)記了cache-control,然后去請(qǐng)求服務(wù)端,然后返回響應(yīng)碼304,表示客戶端本地的緩存數(shù)據(jù)就是最新的
//那么這樣沒(méi)有必要重復(fù)從網(wǎng)絡(luò)上拉數(shù)據(jù),這個(gè)的具體可以去看http自身的緩存機(jī)制
//2.當(dāng)前請(qǐng)求已經(jīng)將響應(yīng)結(jié)果進(jìn)行發(fā)送回調(diào),那么沒(méi)有必要重復(fù)進(jìn)行回調(diào)
//在volley中,request是唯一的
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 當(dāng)前請(qǐng)求已經(jīng)獲得響應(yīng),那么這里進(jìn)行返回報(bào)文的正體解析
Response<?> response = request.parseNetworkResponse(networkResponse);
// 本次請(qǐng)求的響應(yīng)解析已經(jīng)完成
request.addMarker("network-parse-complete");
// 1.當(dāng)前請(qǐng)求可以進(jìn)行緩存
// 2.當(dāng)前緩存的為response中的cacheEntry,這個(gè)需要注意
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 準(zhǔn)備進(jìn)行結(jié)果的分發(fā),那么這里標(biāo)記已經(jīng)分發(fā),為了避免出現(xiàn)重復(fù)分發(fā)的情況
request.markDelivered();
// 將結(jié)果交給分發(fā)者,進(jìn)行結(jié)果的分發(fā)
// 簡(jiǎn)單的一種情況就是將結(jié)果回調(diào)在主線程或者直接在子線程中處理
mDelivery.postResponse(request, response);
//出現(xiàn)異常的時(shí)候,比方說(shuō)網(wǎng)絡(luò)請(qǐng)求一個(gè)找不到的域名之類(lèi)的
//需要進(jìn)行異常的回調(diào),從而通知?jiǎng)e人進(jìn)行處理
} 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);
}
}
}
//...
1.NetworkDispatcher實(shí)際上就是一個(gè)線程,在線程中進(jìn)行無(wú)限循環(huán),不斷地從隊(duì)列中獲取請(qǐng)求進(jìn)行處理
2.為了避免真正意義上的無(wú)限輪詢,應(yīng)該結(jié)合線程的掛起和喚醒機(jī)制進(jìn)行處理,這樣可以靈活做到?jīng)]有任務(wù)的時(shí)候掛起,有任務(wù)到來(lái)的時(shí)候喚醒并且執(zhí)行,這里通過(guò)BlockingQueue作為任務(wù)隊(duì)列的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)這個(gè)效果
3.其實(shí)從NetworkDispatcher中可以看到幾個(gè)任務(wù)的分工,比方說(shuō)緩存、請(qǐng)求發(fā)起者、結(jié)果分發(fā)者,這些在后面會(huì)細(xì)說(shuō)。
這里主要是能夠看出整個(gè)Volley的基礎(chǔ)任務(wù)流程
1.從隊(duì)列中獲取請(qǐng)求
2.檢查當(dāng)前請(qǐng)求是否取消,如果請(qǐng)求已經(jīng)被取消,不進(jìn)行回調(diào)并且善后處理。如果請(qǐng)求沒(méi)有取消,繼續(xù)步驟3
3.通過(guò)request發(fā)起網(wǎng)絡(luò)請(qǐng)求
4.如果當(dāng)前請(qǐng)求的響應(yīng)為本地緩存已經(jīng)是最新,那么此時(shí)可以直接使用本地緩存中的數(shù)據(jù),那么也就不需要進(jìn)行
5.解析服務(wù)器返回的報(bào)文中的body部分
6.當(dāng)前請(qǐng)求是否可以緩存,如果可以緩存,將可以緩存的部分存入緩存中
7.通過(guò)回調(diào)分發(fā)者進(jìn)行分發(fā),最后進(jìn)行回調(diào)(上述步驟中的任何異常都會(huì)進(jìn)行異常回調(diào))
Network
實(shí)際進(jìn)行網(wǎng)絡(luò)請(qǐng)求操作的對(duì)象,通過(guò)接口的模式擴(kuò)展,可以根據(jù)自身的情況實(shí)現(xiàn)不同的請(qǐng)求,比方說(shuō)可以用OkHttp、HttpURLConnection來(lái)做底層的網(wǎng)絡(luò)請(qǐng)求實(shí)現(xiàn)
在Volley中有一個(gè)默認(rèn)的實(shí)現(xiàn)
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
注意到這里還分了兩層,其中一層就是Network,但是看到默認(rèn)實(shí)現(xiàn)為BasicNetwork
,內(nèi)部主要是做一些默認(rèn)操作,比方說(shuō)處理響應(yīng)碼等操作
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
Map<String, String> headers = new HashMap<String, String>();
//這里是在請(qǐng)求之前設(shè)置緩存相關(guān)的頭部
//比方說(shuō)If-Modified-Since,這樣的意義是讓服務(wù)器判斷當(dāng)前本地緩存是否過(guò)期
addCacheHeaders(headers, request.getCacheEntry());
//這里可以看到,實(shí)際的請(qǐng)求交給了HttpStack處理
httpResponse = mHttpStack.performRequest(request, headers);
//成功獲得響應(yīng),這里是獲得請(qǐng)求的響應(yīng)碼
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
//獲得響應(yīng)的頭部報(bào)文
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
//當(dāng)前響應(yīng)碼為304,說(shuō)明當(dāng)前客戶端本地緩存已經(jīng)是最新數(shù)據(jù)
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
//因?yàn)榉?wù)端返回的響應(yīng)碼為304,這意味著客戶端緩存就是最新數(shù)據(jù),并且當(dāng)前服務(wù)端不會(huì)返回?cái)?shù)據(jù)
//那么此時(shí)需要手動(dòng)構(gòu)建響應(yīng)體,從而做到統(tǒng)一的請(qǐng)求響應(yīng)完成
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
entry.responseHeaders.putAll(responseHeaders);
//這里的entry.data其實(shí)就是本地緩存中的數(shù)據(jù)
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// 請(qǐng)求返回重定向的響應(yīng)碼
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
//重定向的鏈接默認(rèn)規(guī)定是在響應(yīng)的頭部報(bào)文的Location字段中,這個(gè)是Http協(xié)議規(guī)定的
String newUrl = responseHeaders.get("Location");
request.setRedirectUrl(newUrl);
}
// 一些響應(yīng)碼比方說(shuō)204雖然也是成功,但是響應(yīng)報(bào)文中是沒(méi)有body的
if (httpResponse.getEntity() != null) {
//網(wǎng)絡(luò)連接實(shí)際上都是一個(gè)socket的連接
//然后客戶端和服務(wù)端分別打開(kāi)輸入和輸出流進(jìn)行數(shù)據(jù)交換
//這里就是將服務(wù)端向客戶端輸入的輸入流轉(zhuǎn)換為字節(jié)數(shù)組
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// 這里只是默認(rèn)非null而已
responseContents = new byte[0];
}
// 這里的時(shí)間為請(qǐng)求發(fā)出和客戶端收到響應(yīng)的時(shí)間
// 一般會(huì)包括請(qǐng)求發(fā)出的延時(shí)、網(wǎng)絡(luò)連接的延時(shí)(DNS解析之類(lèi)的)、服務(wù)端處理延時(shí)等
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode < 200 || statusCode > 299) {
//這里實(shí)際上已經(jīng)處理完了304響應(yīng)碼,304已經(jīng)返回了自己拼裝的數(shù)據(jù)
//這里拋出異常有兩種情況,一個(gè)
throw new IOException();
}
//這里是響應(yīng)成功的正常數(shù)據(jù)
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
int statusCode = 0;
NetworkResponse networkResponse = null;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {//絕大多數(shù)出現(xiàn)異常的時(shí)候會(huì)在這里拋出異常,從而進(jìn)行異常處理回調(diào)
throw new NoConnectionError(e);
}
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl());
} else {
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
}
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 == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
//重定向處理,但是現(xiàn)在DefaultRetryPolicy中是沒(méi)有處理的
attemptRetryOnException("redirect",
request, new RedirectError(networkResponse));
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(e);
}
}
}
}
稍微總結(jié)一下,在BasicNetwork中主要是封裝了緩存的頭部和一些默認(rèn)的請(qǐng)求完成之后的處理,從很大程度上可以降低他人實(shí)現(xiàn)的成本,那么在外部主要是實(shí)現(xiàn)HttpStack即可
這里以Volley默認(rèn)實(shí)現(xiàn)的HurlStack為例
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
//在這里之前,BasicNetwork封裝了緩存的一些頭部報(bào)文
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());//設(shè)置請(qǐng)求預(yù)設(shè)的頭部報(bào)文
map.putAll(additionalHeaders);//為了避免覆蓋,所以BasicNetwork的頭部報(bào)文后設(shè)置
if (mUrlRewriter != null) {
//這里主要是提供一個(gè)機(jī)會(huì)修改請(qǐng)求的實(shí)際url
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
//這里就是創(chuàng)建一個(gè)連接
//1.設(shè)置連接和讀取超時(shí)
//2.如果當(dāng)前是Https請(qǐng)求,允許設(shè)置SSL認(rèn)證工廠
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {//將之前設(shè)置的請(qǐng)求頭部按照http協(xié)議的格式設(shè)置
connection.addRequestProperty(headerName, map.get(headerName));
}
//根據(jù)請(qǐng)求方式,常用的比方說(shuō)POST
//在請(qǐng)求報(bào)文中設(shè)置請(qǐng)求方式為POST
//然后通過(guò)socket連接建立的通路中的OutputStream將請(qǐng)求參數(shù)從客戶端輸出到服務(wù)器中
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();//獲得服務(wù)器的響應(yīng)碼
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
//這里進(jìn)行響應(yīng)體的數(shù)據(jù)綁定
//包括返回?cái)?shù)據(jù)的流、返回?cái)?shù)據(jù)格式和大小等等
response.setEntity(entityFromConnection(connection));
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {//添加響應(yīng)頭部
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
//實(shí)際上在HttpStack中主要做了這樣的幾件事情
//1.實(shí)際發(fā)出請(qǐng)求
//2.根據(jù)相應(yīng)結(jié)果進(jìn)行response的包裝
return response;
}
這個(gè)只是Volley的一個(gè)默認(rèn)實(shí)現(xiàn),在實(shí)際使用中需要根據(jù)自己的需要來(lái)實(shí)現(xiàn)。
ResponseDelivery
當(dāng)一次請(qǐng)求獲得響應(yīng)之后,那么接下來(lái)就要進(jìn)行結(jié)果的發(fā)送和回調(diào),這個(gè)工作就是通過(guò)實(shí)現(xiàn)ResponseDelivery來(lái)完成
這個(gè)主要看接口設(shè)計(jì)就可以明白了
public interface ResponseDelivery {
/**
* 默認(rèn)的使用是這個(gè)方法,用于響應(yīng)網(wǎng)絡(luò)或者緩存獲得的響應(yīng)
*/
public void postResponse(Request<?> request, Response<?> response);
/**
* 后面提供了這個(gè)方法,在postResponse(Request<?> request, Response<?> response)的基礎(chǔ)上
* 添加了一個(gè)runnable用于在響應(yīng)回調(diào)之后進(jìn)行一個(gè)默認(rèn)處理
*/
public void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/**
* 用于發(fā)送異常回調(diào)
*/
public void postError(Request<?> request, VolleyError error);
}
實(shí)際上就是用于發(fā)送成功響應(yīng)的響應(yīng)體或者發(fā)送請(qǐng)求失敗的異常,接下來(lái)看一下默認(rèn)的實(shí)現(xiàn)ExecutorDelivery
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {//實(shí)際上就是把任務(wù)扔進(jìn)handler中執(zhí)行,所以說(shuō)重點(diǎn)在handler的執(zhí)行線程
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
@Override
public void run() {
// 在發(fā)送之前先檢查當(dāng)前任務(wù)是否取消
if (mRequest.isCanceled()) {
//任務(wù)取消則不會(huì)進(jìn)行回調(diào)發(fā)送
mRequest.finish("canceled-at-delivery");
return;
}
//實(shí)際上就是在這里進(jìn)行回調(diào)處理
//在默認(rèn)的場(chǎng)景下,發(fā)出的每一個(gè)請(qǐng)求都需要定義一個(gè)回調(diào)
//那么就是通過(guò)實(shí)現(xiàn)Request中的對(duì)應(yīng)方法實(shí)現(xiàn)
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
//此時(shí)回調(diào)已經(jīng)完成
if (mResponse.intermediate) {
//這個(gè)標(biāo)志意味著當(dāng)前請(qǐng)求沒(méi)有完成,此時(shí)有點(diǎn)尷尬
//默認(rèn)的情況中在NetworkDispatcher中的任務(wù)隊(duì)列中已經(jīng)移除
//但是此時(shí)在RequestQueue中的當(dāng)前請(qǐng)求隊(duì)列中沒(méi)有移除
//這個(gè)目前的應(yīng)用在CacheDispatcher中,成功從緩存中獲取數(shù)據(jù),然后先進(jìn)行回調(diào)
//之后再通過(guò)runnable將當(dāng)前請(qǐng)求放到任務(wù)隊(duì)列中,用于緩存處理后再次發(fā)送請(qǐng)求
mRequest.addMarker("intermediate-response");
} else {
//正常的一次任務(wù)完成之后進(jìn)行收尾處理
mRequest.finish("done");
}
// 目前的使用場(chǎng)景是CacheDispatcher,用于在緩存處理之后再次發(fā)送請(qǐng)求
if (mRunnable != null) {
mRunnable.run();
}
}
在默認(rèn)的設(shè)計(jì)上,設(shè)計(jì)者是希望開(kāi)發(fā)者直接通過(guò)不同線程的Handler,默認(rèn)是主線程的Handler來(lái)進(jìn)行回調(diào)處理。
這個(gè)個(gè)人認(rèn)為還是要按照實(shí)際的場(chǎng)景來(lái)處理,因?yàn)橛械臅r(shí)候一個(gè)請(qǐng)求回來(lái)了可能會(huì)直接接入一個(gè)耗時(shí)操作,可能此時(shí)直接在其它線程池中執(zhí)行相對(duì)合理。
Cache
說(shuō)到緩存,首先就是要從Volley默認(rèn)提供的CacheDispatcher來(lái)時(shí)說(shuō)起,同NetworkDispatcher差不多的意思,只是職責(zé)是處理緩存相關(guān)事宜。
@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();
Request<?> request;
while (true) {
request = null;
try {
// 從緩存請(qǐng)求隊(duì)列中獲取請(qǐng)求,這個(gè)同樣會(huì)阻塞
request = mCacheQueue.take();
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("cache-queue-take");
if (request.isCanceled()) {//當(dāng)前請(qǐng)求已經(jīng)被取消,不需要繼續(xù)從緩存中獲取數(shù)據(jù)等一系列操作
request.finish("cache-discard-canceled");
continue;
}
// 根據(jù)緩存的鍵名從緩存中獲取數(shù)據(jù)
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {//未能命中緩存
request.addMarker("cache-miss");
// 將當(dāng)前任務(wù)添加當(dāng)網(wǎng)絡(luò)請(qǐng)求隊(duì)列中,那么后續(xù)就會(huì)進(jìn)入NetworkDispatcher中執(zhí)行網(wǎng)絡(luò)請(qǐng)求
mNetworkQueue.put(request);
continue;
}
//在http緩存機(jī)制中緩存有兩種過(guò)期
//首先一個(gè)響應(yīng)一定有一個(gè)截止時(shí)間
//但是有個(gè)響應(yīng)可能允許額外的延長(zhǎng)過(guò)期時(shí)間
//這個(gè)具體可以看cache-control
//在Volley中分別被封裝成了entry中的
//softTtl和ttl
if (entry.isExpired()) {//當(dāng)前時(shí)間已經(jīng)大于ttl,那么緩存一定是過(guò)期的,這個(gè)時(shí)間可能包括額外的延長(zhǎng)時(shí)間
//雖然成功擊中緩存,但是當(dāng)前緩存已經(jīng)過(guò)期
//此時(shí)還是去發(fā)起網(wǎng)絡(luò)請(qǐng)求
request.addMarker("cache-hit-expired");
//也許后續(xù)服務(wù)器會(huì)返回304標(biāo)記數(shù)據(jù)沒(méi)有變化,此時(shí)可以直接使用之前的entry
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
//當(dāng)前擊中緩存,并且緩存數(shù)據(jù)沒(méi)有完全過(guò)期
request.addMarker("cache-hit");
//通過(guò)緩存的結(jié)果構(gòu)建response,主要是用于后續(xù)的統(tǒng)一回調(diào)處理
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
//當(dāng)前沒(méi)有完全過(guò)期
if (!entry.refreshNeeded()) {//并且當(dāng)前還沒(méi)有到緩存過(guò)期的時(shí)間
//直接使用緩存結(jié)果進(jìn)行回調(diào)處理即可
mDelivery.postResponse(request, response);
} else {//當(dāng)前已經(jīng)超過(guò)緩存過(guò)期的時(shí)間,不過(guò)卻在額外延長(zhǎng)的時(shí)間內(nèi)
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
final Request<?> finalRequest = request;
//此時(shí)還是認(rèn)為緩存有效并且進(jìn)行回調(diào)
//但是回調(diào)之后還會(huì)發(fā)出網(wǎng)絡(luò)請(qǐng)求,用于刷新緩存數(shù)據(jù)
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
//回調(diào)處理緩存之后,再次發(fā)起網(wǎng)絡(luò)請(qǐng)求
mNetworkQueue.put(finalRequest);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
}
}
}
1.從緩存隊(duì)列中獲取請(qǐng)求,這些添加到隊(duì)列中的請(qǐng)求肯定是允許使用緩存的
2.從緩存中獲取數(shù)據(jù)
3.如果沒(méi)有擊中緩存或者緩存完全過(guò)期,將當(dāng)前任務(wù)放入網(wǎng)絡(luò)請(qǐng)求隊(duì)列中等待執(zhí)行網(wǎng)絡(luò)請(qǐng)求,否則繼續(xù)步驟4
4.當(dāng)前緩存中的數(shù)據(jù)還沒(méi)有到過(guò)期時(shí)間,直接進(jìn)行回調(diào),完成本次請(qǐng)求。如果已經(jīng)超過(guò)過(guò)期時(shí)間,但是還在延長(zhǎng)時(shí)間內(nèi),此時(shí)先進(jìn)行緩存數(shù)據(jù)的回調(diào),然后再將任務(wù)放入網(wǎng)絡(luò)請(qǐng)求隊(duì)列中等待執(zhí)行網(wǎng)絡(luò)請(qǐng)求來(lái)進(jìn)行緩存的刷新和回調(diào)操作
看到這里,基本上的組件已經(jīng)介紹完畢,最后從入口開(kāi)始看一下基礎(chǔ)流程
Volley
作為整個(gè)框架的入口,實(shí)際上就是一個(gè)工廠,里面會(huì)提供一些默認(rèn)選項(xiàng),這里就看默認(rèn)的提供
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
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();//一般都是這里,默認(rèn)使用
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//對(duì)請(qǐng)求執(zhí)行者再包裝一層
Network network = new BasicNetwork(stack);
RequestQueue queue;
//創(chuàng)建請(qǐng)求隊(duì)列管理對(duì)象
if (maxDiskCacheBytes <= -1) {
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
} else {//具有自定義的最大硬盤(pán)緩存大小
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
//開(kāi)始請(qǐng)求隊(duì)列的運(yùn)作
queue.start();
return queue;
}
可以看到,實(shí)際上RequestQueue才是執(zhí)行者,在Volley中主要是初始化硬盤(pán)緩存的目錄,確定請(qǐng)求的執(zhí)行者,初始化請(qǐng)求隊(duì)列管理,并且最終開(kāi)啟隊(duì)列的運(yùn)作,這意味著Volley開(kāi)始運(yùn)作。
RequestQueue
這個(gè)其實(shí)才是Volley對(duì)外的操作對(duì)象,默認(rèn)提供一個(gè)addRequest操作,用于添加一個(gè)請(qǐng)求,從之前的講述可以看到內(nèi)部至少有CacheDispatcher和NetworkDispatcher在工作,看一下處理邏輯:
public void start() {
stop();
//實(shí)際上在volley的架構(gòu)中
//操作都是通過(guò)Dispatcher進(jìn)行的
//不同的Dispatcher的職責(zé)不同
//默認(rèn)使用的只有緩存相關(guān)和請(qǐng)求相關(guān)
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
//實(shí)際上NetworkDispatcher本身就是一個(gè)線程Thread
//默認(rèn)使用的線程固定為4個(gè)
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
public <T> Request<T> add(Request<T> request) {
request.setRequestQueue(this);
synchronized (mCurrentRequests) {//將請(qǐng)求添加到當(dāng)前請(qǐng)求隊(duì)列,這個(gè)主要是用于停止請(qǐng)求的
mCurrentRequests.add(request);
}
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// 當(dāng)前請(qǐng)求不使用緩存
if (!request.shouldCache()) {
//將當(dāng)前請(qǐng)求放入請(qǐng)求隊(duì)列中,等待NetworkDispatcher獲取后執(zhí)行網(wǎng)絡(luò)請(qǐng)求
mNetworkQueue.add(request);
return request;
}
// 當(dāng)前請(qǐng)求需要使用緩存
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();//獲取緩存的鍵名
if (mWaitingRequests.containsKey(cacheKey)) {
//當(dāng)前已經(jīng)有同樣的緩存請(qǐng)求在進(jìn)行中
//將本次請(qǐng)求添加到等待隊(duì)列中
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
//這個(gè)會(huì)等待最早的一個(gè)請(qǐng)求成功之后再處理,具體邏輯在finish中
//簡(jiǎn)單說(shuō)就是最早的一個(gè)請(qǐng)求成功了,然后再將之前等待的任務(wù)全部放入緩存請(qǐng)求隊(duì)列中
//最好當(dāng)然是全部擊中緩存
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// 用于標(biāo)記當(dāng)前請(qǐng)求進(jìn)行中
mWaitingRequests.put(cacheKey, null);
// 將任務(wù)放入緩存請(qǐng)求隊(duì)列中,等待CacheDispatcher獲取后執(zhí)行
mCacheQueue.add(request);
}
return request;
}
}
<T> void finish(Request<T> request) {
synchronized (mCurrentRequests) {//當(dāng)前請(qǐng)求已經(jīng)完成,從當(dāng)前請(qǐng)求中隊(duì)列移除
mCurrentRequests.remove(request);
}
synchronized (mFinishedListeners) {//進(jìn)行請(qǐng)求完成回調(diào)
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
//處理那些等待緩存的請(qǐng)求
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);
}
//將之前等待的任務(wù)全部放入緩存請(qǐng)求隊(duì)列中,后續(xù)最好是全部擊中緩存
mCacheQueue.addAll(waitingRequests);
}
}
}
}
簡(jiǎn)單看一下添加請(qǐng)求的邏輯
1.判斷請(qǐng)求是否使用http緩存,如果不使用,添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中等待NetworkDispatcher獲取后執(zhí)行,否則繼續(xù)步驟2
2.當(dāng)前使用緩存,那么判斷當(dāng)前請(qǐng)求是否有同樣的緩存請(qǐng)求已經(jīng)在發(fā)出中,如果沒(méi)有,將當(dāng)前請(qǐng)求加到緩存請(qǐng)求隊(duì)列中等待CacheDispatcher獲取后執(zhí)行,否則繼續(xù)步驟3
3.添加到等待隊(duì)列中,等待最早的一個(gè)相同的緩存請(qǐng)求完成之后再添加到緩存請(qǐng)求隊(duì)列中,此時(shí)有很大概率可擊中緩存
結(jié)語(yǔ)
實(shí)際上Volley還有下載圖片等功能,不過(guò)可能比起正牌的圖片框架Glide之類(lèi)的還是要弱上不少,基于篇幅限制,還有更多默認(rèn)的功能沒(méi)有講。
主要還是希望能夠理解Volley對(duì)于請(qǐng)求分發(fā)框架的設(shè)計(jì),無(wú)論是對(duì)于直接使用還是寫(xiě)自己的請(qǐng)求框架都是有很大的益處的