轉(zhuǎn)載劉望舒的博客Android網(wǎng)絡(luò)編程(四)從源碼解析Volley
1.Volley結(jié)構(gòu)圖
從上圖可以看到Volley分為三個(gè)線程师逸,分別是主線程蹈垢、緩存調(diào)度線程哆料、和網(wǎng)絡(luò)調(diào)度線程棋枕,首先請(qǐng)求會(huì)加入緩存隊(duì)列,如果發(fā)現(xiàn)可以找到相應(yīng)的緩存結(jié)果就直接讀取緩存并解析息楔,然后回調(diào)給主線程寝贡;如果在緩存中沒有找到結(jié)果,則將這條請(qǐng)求加入到網(wǎng)絡(luò)隊(duì)列中值依,然后發(fā)送HTTP請(qǐng)求兔甘,解析響應(yīng)并寫入緩存,并回調(diào)給主線程鳞滨。
2.從RequestQueue入手
我們都知道使用Volley之前首先要?jiǎng)?chuàng)建RequestQueue:
RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
這也是volley運(yùn)作的入口,看看newRequestQueue:
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (HttpStack)null);
}
public static RequestQueue newRequestQueue(Context context, HttpStack stack){
return newRequestQueue(context, stack, -1);
}
連續(xù)調(diào)用了兩個(gè)重載函數(shù)蟆淀,最終調(diào)用的是:
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), "volley");
String userAgent = "volley/0";
try {
String network = context.getPackageName();
PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
userAgent = network + "/" + queue.versionCode;
}
catch (NameNotFoundException var7) {
;
}
if(stack == null) {
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack(); }
else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
RequestQueue queue1;
if(maxDiskCacheBytes <= -1) {
queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
} else {
queue1 = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network1);
}
queue1.start(); return queue1;
}
可以看到如果Android版本大于等于2.3則調(diào)用基于HttpURLConnection的HurlStack拯啦,否則就調(diào)用基于HttpClient的HttpClientStack。并創(chuàng)建了RequestQueue熔任,調(diào)用了start()方法:
public void start() {
this.stop();
this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
this.mCacheDispatcher.start();
for(int i = 0; i < this.mDispatchers.length; ++i) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery); this.mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
CacheDispatcher是緩存調(diào)度線程褒链,并調(diào)用了start()方法,在循環(huán)中調(diào)用了NetworkDispatcher的start()方法疑苔,NetworkDispatcher是網(wǎng)絡(luò)調(diào)度線程甫匹,默認(rèn)情況下mDispatchers.length為4,默認(rèn)開啟了4個(gè)網(wǎng)絡(luò)調(diào)度線程惦费,也就是說有5個(gè)線程在后臺(tái)運(yùn)行并等待請(qǐng)求的到來兵迅。接下來我們創(chuàng)建各種的Request,并調(diào)用RequestQueue的add()方法:
public <T> Request<T> add(Request<T> request) {
request.setRequestQueue(this);
Set var2 = this.mCurrentRequests;
synchronized(this.mCurrentRequests) {
this.mCurrentRequests.add(request);
}
request.setSequence(this.getSequenceNumber());
request.addMarker("add-to-queue"); //如果不能緩存薪贫,則將請(qǐng)求添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中
if(!request.shouldCache()) {
this.mNetworkQueue.add(request);
return request;
} else {
Map var8 = this.mWaitingRequests;
synchronized(this.mWaitingRequests) {
String cacheKey = request.getCacheKey();
//之前是否有執(zhí)行相同的請(qǐng)求且還沒有返回結(jié)果的恍箭,如果有的話將此請(qǐng)求加入mWaitingRequests隊(duì)列,不再重復(fù)請(qǐng)求
if(this.mWaitingRequests.containsKey(cacheKey)) {
Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
if(stagedRequests == null) {
stagedRequests = new LinkedList();
}
((Queue)stagedRequests).add(request);
this.mWaitingRequests.put(cacheKey, stagedRequests);
if(VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
}
} else { //沒有的話就將請(qǐng)求加入緩存隊(duì)列mCacheQueue瞧省,同時(shí)加入mWaitingRequests中用來做下次同樣請(qǐng)求來時(shí)的重復(fù)判斷依據(jù)
this.mWaitingRequests.put(cacheKey, (Object)null); this.mCacheQueue.add(request);
}
return request;
}
}
}
通過判斷request.shouldCache()扯夭,來判斷是否可以緩存,默認(rèn)是可以緩存的鞍匾,如果不能緩存交洗,則將請(qǐng)求添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中,如果能緩存就判斷之前是否有執(zhí)行相同的請(qǐng)求且還沒有返回結(jié)果的橡淑,如果有的話將此請(qǐng)求加入mWaitingRequests隊(duì)列构拳,不再重復(fù)請(qǐng)求;沒有的話就將請(qǐng)求加入緩存隊(duì)列mCacheQueue,同時(shí)加入mWaitingRequests中用來做下次同樣請(qǐng)求來時(shí)的重復(fù)判斷依據(jù)隐圾。
從上面可以看出RequestQueue的add()方法并沒有做什么請(qǐng)求網(wǎng)絡(luò)或者對(duì)緩存進(jìn)行操作伍掀。當(dāng)將請(qǐng)求添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列或者緩存隊(duì)列時(shí),這時(shí)在后臺(tái)的網(wǎng)絡(luò)調(diào)度線程和緩存調(diào)度線程輪詢各自的請(qǐng)求隊(duì)列發(fā)現(xiàn)有請(qǐng)求任務(wù)則開始執(zhí)行暇藏,我們先看看緩存調(diào)度線程蜜笤。
3.CacheDispatcher緩存調(diào)度線程
CacheDispatcher的run()的方法:
public void run() {
if(DEBUG) {
VolleyLog.v("start new dispatcher", new Object[0]);
}
//線程優(yōu)先級(jí)設(shè)置為最高級(jí)別
Process.setThreadPriority(10);
this.mCache.initialize();
while(true) {
while(true) {
while(true) {
while(true) {
try {
//獲取緩存隊(duì)列中的一個(gè)請(qǐng)求
final Request e = (Request)this.mCacheQueue.take();
e.addMarker("cache-queue-take");
//如果請(qǐng)求取消了則將請(qǐng)求停止掉
if(e.isCanceled()) {
e.finish("cache-discard-canceled");
} else {
//查看是否有緩存的響應(yīng)
Entry entry = this.mCache.get(e.getCacheKey());
//如果緩存響應(yīng)為空,則將請(qǐng)求加入網(wǎng)絡(luò)請(qǐng)求隊(duì)列
if(entry == null) {
e.addMarker("cache-miss");
this.mNetworkQueue.put(e);
//判斷緩存響應(yīng)是否過期
} else if(!entry.isExpired()) {
e.addMarker("cache-hit");
//對(duì)數(shù)據(jù)進(jìn)行解析并回調(diào)給主線程
Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
e.addMarker("cache-hit-parsed");
if(!entry.refreshNeeded()) {
this.mDelivery.postResponse(e, response);
} else {
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 {
e.addMarker("cache-hit-expired");
e.setCacheEntry(entry);
this.mNetworkQueue.put(e);
}
}
} catch (InterruptedException var4) {
if(this.mQuit) {
return;
}
}
}
}
}
}
}
static {
DEBUG = VolleyLog.DEBUG;
}
看到四個(gè)while循環(huán)有些暈吧盐碱,讓我們挑重點(diǎn)的說把兔,首先從緩存隊(duì)列取出請(qǐng)求,判斷是否請(qǐng)求是否被取消了瓮顽,如果沒有則判斷該請(qǐng)求是否有緩存的響應(yīng)县好,如果有并且沒有過期則對(duì)緩存響應(yīng)進(jìn)行解析并回調(diào)給主線程。接下來看看網(wǎng)絡(luò)調(diào)度線程暖混。
4.NetworkDispatcher網(wǎng)絡(luò)調(diào)度線程
NetworkDispatcher的run()方法:
public void run() {
Process.setThreadPriority(10);
while(true) {
long startTimeMs;
Request request;
while(true) {
startTimeMs = SystemClock.elapsedRealtime();
try {
//從隊(duì)列中取出請(qǐng)求
request = (Request)this.mQueue.take();
break;
} catch (InterruptedException var6) {
if(this.mQuit) {
return;
}
}
}
try {
request.addMarker("network-queue-take");
if(request.isCanceled()) {
request.finish("network-discard-cancelled");
} else {
this.addTrafficStatsTag(request);
//請(qǐng)求網(wǎng)絡(luò)
NetworkResponse e = this.mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if(e.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
} else {
Response volleyError1 = request.parseNetworkResponse(e);
request.addMarker("network-parse-complete");
if(request.shouldCache() && volleyError1.cacheEntry != null) {
//將響應(yīng)結(jié)果存入緩存
this.mCache.put(request.getCacheKey(), volleyError1.cacheEntry);
request.addMarker("network-cache-written");
}
request.markDelivered();
this.mDelivery.postResponse(request, volleyError1);
}
}
} catch (VolleyError var7) {
var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
this.parseAndDeliverNetworkError(request, var7);
} catch (Exception var8) {
VolleyLog.e(var8, "Unhandled exception %s", new Object[]{var8.toString()});
VolleyError volleyError = new VolleyError(var8);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
this.mDelivery.postError(request, volleyError);
}
}
}
網(wǎng)絡(luò)調(diào)度線程也是從隊(duì)列中取出請(qǐng)求并且判斷是否被取消了缕贡,如果沒取消就去請(qǐng)求網(wǎng)絡(luò)得到響應(yīng)并回調(diào)給主線程。請(qǐng)求網(wǎng)絡(luò)時(shí)調(diào)用this.mNetwork.performRequest(request)拣播,這個(gè)mNetwork是一個(gè)接口晾咪,實(shí)現(xiàn)它的類是BasicNetwork,我們來看看BasicNetwork的performRequest()方法:
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());
httpResponse = this.mHttpStack.performRequest(request, e);
StatusLine statusCode1 = httpResponse.getStatusLine();
int networkResponse1 = statusCode1.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
if(networkResponse1 == 304) {
Entry requestLifetime2 = request.getCacheEntry();
if(requestLifetime2 == null) {
return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
}
requestLifetime2.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(304, requestLifetime2.data, requestLifetime2.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
}
...省略
從上面可以看到在12行調(diào)用的是HttpStack的performRequest()方法請(qǐng)求網(wǎng)絡(luò)贮配,接下來根據(jù)不同的響應(yīng)狀態(tài)碼來返回不同的NetworkResponse谍倦。另外HttpStack也是一個(gè)接口,實(shí)現(xiàn)它的兩個(gè)類我們?cè)谇懊嬉呀?jīng)提到了就是HurlStack和HttpClientStack泪勒。讓我們?cè)倩氐絅etworkDispatcher昼蛀,請(qǐng)求網(wǎng)絡(luò)后,會(huì)將響應(yīng)結(jié)果存在緩存中圆存,如果響應(yīng)結(jié)果成功則調(diào)用this.mDelivery.postResponse(request, volleyError1)來回調(diào)給主線程叼旋。來看看Delivery的postResponse()方法:
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable));
}
來看看ResponseDeliveryRunnable里面做了什么:
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);
} else {
this.mRequest.deliverError(this.mResponse.error);
}
if(this.mResponse.intermediate) {
this.mRequest.addMarker("intermediate-response");
} else {
this.mRequest.finish("done");
}
if(this.mRunnable != null) {
this.mRunnable.run();
}
}
}
}
第17行調(diào)用了this.mRequest.deliverResponse(this.mResponse.result),這個(gè)就是實(shí)現(xiàn)Request抽象類必須要實(shí)現(xiàn)的方法沦辙,我們來看看StringRequest的源碼:
public class StringRequest extends Request<String> {
private final Listener<String> mListener;
public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) {
super(method, url, errorListener);
this.mListener = listener;
}
public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
this(0, url, listener, errorListener);
}
protected void deliverResponse(String response) {
this.mListener.onResponse(response);
}
...省略
}
在deliverResponse方法中調(diào)用了this.mListener.onResponse(response)送淆,最終將response回調(diào)給了Response.Listener的onResponse()方法。我們用StringRequest請(qǐng)求網(wǎng)絡(luò)的寫法是這樣的:
RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest mStringRequest = new StringRequest(Request.Method.GET, "http://www.baidu.com",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.i("wangshu", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("wangshu", error.getMessage(), error);
}
});
//將請(qǐng)求添加在請(qǐng)求隊(duì)列中
mQueue.add(mStringRequest);