導(dǎo)語
閱讀源碼是已經(jīng)入門的Android開發(fā)者必經(jīng)之路啡捶,這是提高自己對代碼理解的一步利凑。但是一開始閱讀源碼不能深入的去讀細(xì)節(jié)部分卸勺,而是理順源碼的主要部分砂沛,理解它的實(shí)現(xiàn)原理即可,否則一旦陷入會(huì)導(dǎo)致很難讀懂曙求,可能會(huì)失去閱讀源碼的興趣碍庵。volley現(xiàn)在已經(jīng)不是主流的網(wǎng)絡(luò)請求框架,但是其結(jié)構(gòu)并不復(fù)雜悟狱,容易理解静浴,因此選擇此框架來學(xué)習(xí)。
一挤渐、Volley的使用
RequestQueue requestQueue =
Volley.newRequestQueue(context.getApplicationContext());// 創(chuàng)建請求隊(duì)列實(shí)例
StringRequest request = new StringRequest(url,
new Listener<String>() {
@Override
public void onResponse(String response) { // 成功返回?cái)?shù)據(jù)苹享,做UI更新等操作},
new ErrorListener() {
@Override
public void onErrorResponse(VolleyError arg0) {// 失敗,做相應(yīng)處理}
});
requestQueue.add(request);// 將請求添加至請求隊(duì)列中
以上是volley常規(guī)的使用方式浴麻。先是創(chuàng)建請求隊(duì)列得问,這個(gè)一般在采用單例模式創(chuàng)建然后用于全局;接著創(chuàng)建請求软免,請求有StringRequest宫纬、JsonObjectRequest等等,主要區(qū)別就是返回?cái)?shù)據(jù)格式不同膏萧。最后將請求添加至請求隊(duì)列(循環(huán)處理請求)漓骚。
二、Volley解析
閱讀源碼的方法很簡單榛泛,就是跟進(jìn)代碼认境,一點(diǎn)點(diǎn)理解。我們從創(chuàng)建請求隊(duì)列實(shí)例開始跟進(jìn)挟鸠。(可能會(huì)省略部分不重要的源碼)
2.1 Volley
跟進(jìn)Volley.newRequestQueue(context.getApplicationContext()),可以發(fā)現(xiàn)此類是volley框架的入口亩冬,源碼如下:
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), "volley");
if(stack == null) {// 在api9以前使用httpClient網(wǎng)絡(luò)請求艘希,而api9以后使用httpUrlConnection,但是在api23以后已經(jīng)不支持httpClient了
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack();// 用httpUrlConnection請求并獲取結(jié)果
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));// 用httpClient請求并獲取結(jié)果
}
}
BasicNetwork network1 = new BasicNetwork((HttpStack)stack);// 處理請求硅急,使用httpStack獲得請求結(jié)果封裝后返回
RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);// 創(chuàng)建請求隊(duì)列覆享,并傳入緩存對象以及處理請求對象
queue1.start();// 啟動(dòng)了緩存線程以及網(wǎng)絡(luò)請求線程
return queue1;
}
2.2 RequestQueue
我們先跟進(jìn)queue1.start(),讀源碼的時(shí)候不能一直按順序讀营袜,如果先跟進(jìn)BasicNetWork或DiskBasedCache會(huì)很難理解撒顿,但是我們能從語義上基本能看出他們的作用,一個(gè)是網(wǎng)絡(luò)請求一個(gè)是磁盤緩存荚板。queue1.start()能看出是隊(duì)列啟動(dòng)的入口凤壁,從這里進(jìn)入會(huì)遇到使用BasicNetWork或DiskBasedCache這些類的時(shí)候吩屹。
public void start() {
this.stop();
this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue,
this.mNetworkQueue, this.mCache, this.mDelivery);// 這是一個(gè)緩存線程,已緩存結(jié)果的一些請求不必再去進(jìn)行網(wǎng)絡(luò)請求拧抖,直接在mCacheQueue里拿結(jié)果煤搜,同時(shí)緩存會(huì)有過期、新鮮度等判斷唧席,如果不符合條件則取mNetworkQueue里的網(wǎng)絡(luò)請求擦盾。mCache是緩存結(jié)果的一個(gè)實(shí)例,mDelivery是分發(fā)結(jié)果的實(shí)例淌哟。
this.mCacheDispatcher.start();
for(int i = 0; i < this.mDispatchers.length; ++i) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue,
this.mNetwork, this.mCache, this.mDelivery);// 網(wǎng)絡(luò)請求線程迹卢,網(wǎng)絡(luò)請求時(shí)間長,因此需要多個(gè)線程一起執(zhí)行徒仓,默認(rèn)是4個(gè)腐碱,可以自己設(shè)置。
this.mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
2.3 NetworkDispatcher
先講解NetworkDispatcher是因?yàn)槲蚁胍鶕?jù)一開始執(zhí)行的流程走蓬衡, 只有進(jìn)行過網(wǎng)絡(luò)請求的才有結(jié)果緩存喻杈,這樣思路比較順點(diǎn)。NetworkDispatcher是一個(gè)處理網(wǎng)絡(luò)請求的線程狰晚。
public void run() {
while(true) {
long startTimeMs;
Request request;
while(true) {// 只要網(wǎng)絡(luò)請求隊(duì)列里出現(xiàn)請求筒饰,馬上取出執(zhí)行。
startTimeMs = SystemClock.elapsedRealtime();
try {
request = (Request)this.mQueue.take();// 從隊(duì)列拿出請求
break;
} catch (InterruptedException var6) {
if(this.mQuit) {
return;
}
}
}
try {
request.addMarker("network-queue-take");
if(request.isCanceled()) {// 這些地方我們就不必去深究壁晒,只要語義上理解就好瓷们,很明顯這是請求取消了,然后請求完成秒咐。傳入了一些標(biāo)志的字符串谬晕。
request.finish("network-discard-cancelled");
} else {
NetworkResponse e = this.mNetwork.performRequest(request);// 這里就是關(guān)鍵的網(wǎng)絡(luò)請求,拿到封裝好的結(jié)果(其實(shí)是將httpResponse解析成自定義的NetworkResponse)
request.addMarker("network-http-complete");
if(e.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
} else {
Response volleyError1 = request.parseNetworkResponse(e);// 再次將NetWorkResponse對象轉(zhuǎn)換成自定義的Response對象携取。Response最終可解析成我們需要的數(shù)據(jù)攒钳,而NetworkResonse只是過渡的一個(gè)對象。
request.addMarker("network-parse-complete");
if(request.shouldCache() && volleyError1.cacheEntry != null) {
this.mCache.put(request.getCacheKey(),
volleyError1.cacheEntry);// 這里就是緩存結(jié)果
request.addMarker("network-cache-written");
}
request.markDelivered();
this.mDelivery.postResponse(request, volleyError1);// 這也是關(guān)鍵雷滋,將結(jié)果分發(fā)給request不撑,request再傳給監(jiān)聽器回調(diào)給主線程
}
}
} catch (VolleyError var7) {
var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
this.parseAndDeliverNetworkError(request, var7);
} catch (Exception var8) {
VolleyError volleyError = new VolleyError(var8);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
this.mDelivery.postError(request, volleyError);
}
}
}
2.4 BasicNetwork
該類實(shí)現(xiàn)了Network接口,主要的作用是進(jìn)行網(wǎng)絡(luò)請求并返回結(jié)果晤斩。 跟進(jìn)以上代碼的mNetwork.performRequest(request)
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while(true) {
HttpResponse httpResponse = null;
Object responseContents = null;
Map responseHeaders = Collections.emptyMap();
try {
HashMap e = new HashMap();
this.addCacheHeaders(e, request.getCacheEntry());// 取出request中的緩存實(shí)體焕檬,將部分信息緩存到緩存頭中(可能無法理解為什么要有緩存頭,不用死纏爛打澳泵,先看下面)
httpResponse = this.mHttpStack.performRequest(request, e);// 這里就是真正的請求數(shù)據(jù)并返回結(jié)果实愚。
StatusLine statusCode2 = httpResponse.getStatusLine();
int networkResponse1 = statusCode2.getStatusCode();// 狀態(tài)碼,用于判斷服務(wù)端的結(jié)果是否改變
responseHeaders = convertHeaders(httpResponse.getAllHeaders());// 解析返回結(jié)果的響應(yīng)頭
if(networkResponse1 != 304) {// 304表示在一定時(shí)間內(nèi)結(jié)果是否有改動(dòng)
byte[] responseContents1;
if(httpResponse.getEntity() != null) {
responseContents1 = this.entityToBytes(httpResponse.getEntity());// 將httpEntry實(shí)體轉(zhuǎn)換成字節(jié)數(shù)組
} else {
responseContents1 = new byte[0];
}
long requestLifetime1 = SystemClock.elapsedRealtime() - requestStart;
this.logSlowRequests(requestLifetime1, request, responseContents1, statusCode2);
if(networkResponse1 >= 200 && networkResponse1 <= 299) {
return new NetworkResponse(networkResponse1, responseContents1, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);// 返回一個(gè)根據(jù)服務(wù)端返回結(jié)果來構(gòu)建的NetworkResponse對象
}
throw new IOException();
}
Entry requestLifetime = request.getCacheEntry();// 執(zhí)行到這里說明服務(wù)端沒有改動(dòng)結(jié)果,因此直接取緩存
if(requestLifetime == null) {
return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
}
requestLifetime.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(304, requestLifetime.data, requestLifetime.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException var12) {
}
}
}
2.5 HurlStack
很明顯腊敲,我們要跟進(jìn)核心部分mHttpStack.performRequest(request, e)击喂,這里是通過接口的向上轉(zhuǎn)型,我拿HurlStack實(shí)現(xiàn)類來講解兔仰,因?yàn)镠ttpStack已經(jīng)基本用不到了茫负。
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap map = new HashMap();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
//這部分是參數(shù)的拼接,準(zhǔn)備用來請求
URL parsedUrl1 = new URL(url);
HttpURLConnection connection = this.openConnection(parsedUrl1, request);
Iterator responseCode = map.keySet().iterator();
while(responseCode.hasNext()) {
String protocolVersion = (String)responseCode.next();
connection.addRequestProperty(protocolVersion, (String)map.get(protocolVersion));
}
setConnectionParametersForRequest(connection, request);
ProtocolVersion protocolVersion1 = new ProtocolVersion("HTTP", 1, 1);
int responseCode1 = connection.getResponseCode();
if(responseCode1 == -1) {
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
} else {
BasicStatusLine responseStatus = new BasicStatusLine(protocolVersion1, connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));// 這句才是真正的網(wǎng)絡(luò)請求乎赴,返回結(jié)果后賦予response
Iterator var12 = connection.getHeaderFields().entrySet().iterator();
while(var12.hasNext()) {
Entry header = (Entry)var12.next();
if(header.getKey() != null) {
BasicHeader h = new BasicHeader((String)header.getKey(), (String)((List)header.getValue()).get(0));
response.addHeader(h);
}
}
return response;
}
}
2.6 ExecutorDelivery
在通過HurlStack獲取HttpResponse結(jié)果后忍法,我們返回到BasicNetwork繼續(xù)讀代碼,發(fā)現(xiàn)將HttpResponse轉(zhuǎn)換成NetworkResponse并且返回到NetworkDispatcher榕吼,最后執(zhí)行了mDelivery.postResponse(request, volleyError1)分發(fā)結(jié)果饿序。
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
//調(diào)用了自定義的Runnable,其實(shí)是將子線程切換到了主線程
this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable));
}
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) {
this.mRequest = request;
this.mResponse = response;
this.mRunnable = runnable;
}
public void run() {
if(this.mRequest.isCanceled()) {// 請求取消
this.mRequest.finish("canceled-at-delivery");
} else {
if(this.mResponse.isSuccess()) {
this.mRequest.deliverResponse(this.mResponse.result);// 分發(fā)結(jié)果給request羹蚣,request最終分發(fā)給監(jiān)聽器
} else {
this.mRequest.deliverError(this.mResponse.error);
}
}
}
}
2.7 CacheDispatcher
上面已經(jīng)完成了一個(gè)完整的網(wǎng)絡(luò)請求并且將結(jié)果分發(fā)給監(jiān)聽器原探,供開發(fā)者使用結(jié)果。在之前我們先講解了NetworkDispatcher顽素,并且也看到了mCache.put(request.getCacheKey(), volleyError1.cacheEntry)咽弦,將緩存實(shí)體存入緩存對象中。CacheDispatcher也是一個(gè)線程胁出,循環(huán)來取緩存隊(duì)列里的請求:
public void run() {
//省略部分代碼...
this.mCache.initialize();// 緩存類初始化型型,主要是在SD卡里創(chuàng)建緩存文件夾
while(true) {
final Request e = (Request)this.mCacheQueue.take();// 拿出緩存請求
e.addMarker("cache-queue-take");
if(e.isCanceled()) {// 請求取消
e.finish("cache-discard-canceled");
} else {
Cache.Entry entry = this.mCache.get(e.getCacheKey());// 根據(jù)key拿到緩存實(shí)體
if(entry == null) {
e.addMarker("cache-miss");
this.mNetworkQueue.put(e);// 如果沒有緩存則加入網(wǎng)絡(luò)請求隊(duì)列進(jìn)行網(wǎng)絡(luò)請求
} else if(entry.isExpired()) {// 過期的也要加入網(wǎng)絡(luò)請求隊(duì)列
e.addMarker("cache-hit-expired");
e.setCacheEntry(entry);
this.mNetworkQueue.put(e);
} else {
e.addMarker("cache-hit");
Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));// 將NetworkResponse轉(zhuǎn)換成Response
e.addMarker("cache-hit-parsed");
if(entry.refreshNeeded()) {// 新鮮度判斷,如果需要刷新則在分發(fā)結(jié)果的同時(shí)加入網(wǎng)絡(luò)請求隊(duì)列全蝶,去獲取最新的結(jié)果
e.addMarker("cache-hit-refresh-needed");
e.setCacheEntry(entry);
response.intermediate = true;
this.mDelivery.postResponse(e, response, new Runnable() {
public void run() {
try {
CacheDispatcher.this.mNetworkQueue.put(e);
} catch (InterruptedException var2) {
;
}
}
});
} else {
this.mDelivery.postResponse(e, response);// 分發(fā)結(jié)果(和NetworkDispatcher一樣)
}
}
}
}
}
三闹蒜、總結(jié)
隨著一點(diǎn)點(diǎn)的跟進(jìn)代碼,我們理清了Volley的工作流程:
1.初始化網(wǎng)絡(luò)請求類(BasicNetwork)抑淫、緩存類(DiskBasedCache)等绷落,創(chuàng)建請求隊(duì)列(RequestQueue)并啟動(dòng)緩存請求線程(CacheDispatcher)和網(wǎng)絡(luò)請求線程(NetworkDispatcher )。
2.CacheDispatcher和NetworkDispatcher不斷輪詢?nèi)〕稣埱笫嘉绻芯彺鎰t進(jìn)行過期時(shí)間砌烁、新鮮度等判斷,來決定是從Cache中取緩存還是再去進(jìn)行網(wǎng)絡(luò)請求催式。網(wǎng)絡(luò)請求通過BasicNetwork調(diào)用HttpStack來返回請求結(jié)果后會(huì)進(jìn)行緩存(除非你自己設(shè)置不進(jìn)行緩存)函喉。
3.最后通過ExecutorDelivery實(shí)現(xiàn)類分發(fā)給我們創(chuàng)建的Request中,而根據(jù)我們使用的Request(StringRequest蓄氧、JsonRequest、ImageRequest等)返回對應(yīng)的數(shù)據(jù)結(jié)構(gòu)槐脏。
以上就是Volley的主要工作原理喉童,我覺得先看懂主要原理是重要的一步,理解原理后再去看代碼細(xì)節(jié),比如Volley中的PoolingByteArrayOutputStream(繼承ByteArrayOutputStream并給二進(jìn)制輸出流增加緩存區(qū))堂氯、HttpHeaderParser(用來解析返回結(jié)果的頭部)等蔑担。
參考:
http://a.codekk.com/detail/Android/grumoon/Volley%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90