序
wuliVolley
請求框架出來已經(jīng)許久許久单料,小白的我埋凯,以前只知其一,而不知其二扫尖。 現(xiàn)在才開始學(xué)習(xí)他的源碼白对,慚愧慚愧,既然都落后了换怖,當(dāng)然得乘勝追擊學(xué)起來甩恼,否則只能永遠(yuǎn)落后了(放了一波毒雞湯)。希望能學(xué)習(xí)其中的編程思想以及了解它是如何完成整個(gè)請求以及實(shí)現(xiàn)磁盤緩存的。本文我將通過一個(gè)分析StringRequest
的請求來完整的剖析一下其中涉及到的重要代碼条摸。我的初衷一直都沒有變悦污,那就是每寫一篇文章是為了給自己的知識(shí)庫添上一點(diǎn),也是讓自己堅(jiān)持學(xué)習(xí)钉蒲,同時(shí)給學(xué)習(xí)路上的你我他提供一點(diǎn)參考的資料切端。 本文將會(huì)有點(diǎn)長,別失去耐心顷啼,好好看下去踏枣。我相信肯定能讓你對Volley
更了解。 Are you Ready钙蒙? Go!
要學(xué)習(xí)一個(gè)源碼茵瀑,我們可以首先從我們使用的地方出發(fā)去學(xué)習(xí),這樣更好理解躬厌,也不至于一下子迷失在源碼的汪洋大海中瘾婿。所以我們先看看我們怎么用它。當(dāng)請求一個(gè)string的時(shí)候烤咧,我們會(huì):
// 1.new一個(gè)RequestQueue
RequestQueue mRequestQueue = Volley.newRequestQueue(context);
//2.new一個(gè)StringRequest對象偏陪,傳入Response.Listener 和Response.ErrorListener來監(jiān)聽請求成功和請求失敗的時(shí)候的回調(diào)
StringRequest request = new StringRequest("http://www.baidu.com", new Response.Listener<String>() {
@Override
public void onResponse(String s) {
txt_response.setText(s);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Log.e("onError", volleyError.getMessage());
}
});
//將請求加入到請求隊(duì)列中
mRequstQueue.add(request);
RequestQueue
正如它的字面意思一樣就是一個(gè)請求隊(duì)列。每當(dāng)有一個(gè)新的請求的時(shí)候就將請求添加進(jìn)去煮嫌。讓我們看看Volley.newRequstQueue
做了些什么笛谦。
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (HttpStack)null);
}
可以看到它調(diào)用了另外一個(gè)方法,傳入了一個(gè)null的HttpStack昌阿,這個(gè)是什么呢饥脑?
Volley提供了HttpStack
的兩個(gè)實(shí)現(xiàn)子類,HttpClientStack 與 HurlStack HttpClientStack 和HurlStack是兩種網(wǎng)絡(luò)請求的封裝類.當(dāng)Android的SDK版本號(hào)小于9的時(shí)候懦冰,使用的是HttpStack來進(jìn)行網(wǎng)絡(luò)請求灶轰,而HttpClientStack是通過HttpClient來請求網(wǎng)絡(luò)數(shù)據(jù)的。相反刷钢,當(dāng)sdk大于9時(shí)笋颤,使用HurlStack來請求網(wǎng)絡(luò)數(shù)據(jù),HurlStack是通過HttpURLConnection來進(jìn)行網(wǎng)絡(luò)連接并請求數(shù)據(jù)的内地。具體是如何請求數(shù)據(jù)的這里我們就不講解了伴澄,簡單提一下就是通過HttpClient
或者HttpUrlConnection
來請求數(shù)據(jù)獲得Response
. 感興趣的可以自己在源碼中找到對應(yīng)的類查看一下。
接著往下看:
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @param stack An {@link HttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
//定義一個(gè)緩存目錄阱缓,默認(rèn)是在應(yīng)用的緩存目錄下創(chuàng)建一個(gè)叫volley的目錄用于存儲(chǔ)緩存
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) {
//如果stack是null非凌,則根據(jù)SDK的版本來創(chuàng)建對應(yīng)的HttpStack的子類
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack(); //通過HttpUrlConnection請求
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
//通過HttpClient連接請求網(wǎng)絡(luò)數(shù)據(jù)
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//傳入HttpStack,創(chuàng)建一個(gè)Network對象荆针,在我看來Network類是對HttpStack的進(jìn)一步封裝敞嗡,添加了對請求結(jié)果的一些處理颁糟,實(shí)際網(wǎng)絡(luò)請求是通過HttpStack實(shí)現(xiàn)的。
Network network = new BasicNetwork(stack);
//創(chuàng)建并啟動(dòng)一個(gè)RequestQueue
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
我已經(jīng)在代碼中添加了對應(yīng)的注釋喉悴,主要就是創(chuàng)建了HttpStack
對象傳遞給Network
的構(gòu)造函數(shù)棱貌,然后用于網(wǎng)絡(luò)請求,之后通過Network
以及DiskBasedCache
構(gòu)造一個(gè)RequestQueue
并start
粥惧。在這里DiskBasedCache
是一個(gè)默認(rèn)的管理磁盤緩存的一個(gè)類,用于緩存請求的結(jié)果最盅。Volley
的緩存機(jī)制就是通過這個(gè)實(shí)現(xiàn)的突雪,****需要注意的是因?yàn)閂olley的緩存都是緩存在磁盤里面的所以緩存讀取速度要比內(nèi)存緩存更慢****。當(dāng)然你也可以改成自己要的內(nèi)存緩存涡贱,這也是我們學(xué)習(xí)源碼的一個(gè)目的咏删,不僅知道別人是怎么寫的旺矾,更重要的是能夠靈活運(yùn)用并且將其改造成我們需要的樣子榄檬。
接下來我們來看一下RequestQueue
的構(gòu)造函數(shù)和start
都做了些什么,為什么start
之后嚣镜,然后只需要將Request
加入隊(duì)列就開始請求數(shù)據(jù)并且得到對應(yīng)的Response
激挪。
通過追蹤代碼辰狡,可以知道最終調(diào)用了這個(gè)構(gòu)造函數(shù)。
/**
創(chuàng)建一個(gè)工作池垄分,只有start方法調(diào)用了之后宛篇,進(jìn)程才會(huì)開始
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
* @param delivery A ResponseDelivery interface for posting responses and errors
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
在上面的英文注釋當(dāng)中已經(jīng)說得很清楚了,Cache
就是用于將Http請求的Response
存儲(chǔ)到磁盤薄湿,Network
用于執(zhí)行Http請求叫倍。NetworkDispatcher
是一個(gè)線程,用于網(wǎng)絡(luò)請求的分發(fā)豺瘤。這里我們默認(rèn)是開啟了4個(gè)NetworkDispatcher
.ResponseDelivery
是用于分發(fā)請求的錯(cuò)誤和響應(yīng)的吆倦。
看完了構(gòu)造函數(shù)我們就來看看start()
方法吧。
/**
* Starts the dispatchers in this queue.
*/
public void start() {
//首先將確保所有CacheDispatcher和NetworkDispatcher都停止
stop(); // Make sure any currently running dispatchers are stopped.
//接下來開啟CacheDispatcher和NetworkDispatcher線程
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
這里面的代碼雖然不多坐求,但是確是萬物之源蚕泽。 因?yàn)樗_啟了CacheDispatcher
和NetworkDispatcher
線程,然后開始了監(jiān)聽網(wǎng)絡(luò)請求的工作桥嗤。那么Cache Dispatcher
和NetworkDispatcher
分別做了哪些工作呢赛糟。
這里我們先來看看NetworkDispatcher
,上面我們說到過它是一個(gè)線程,所以我們先看看它的構(gòu)造方法砸逊,然后直擊他的run方法璧南。
/**
* Creates a new network dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*
* @param queue Queue of incoming requests for triage
* @param network Network interface to use for performing requests
* @param cache Cache interface to use for writing responses to cache
* @param delivery Delivery interface to use for posting responses
*/
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
從上面可以知道,NetworkDispathcer
擁有一個(gè)BlockingQueue
阻塞式隊(duì)列的一個(gè)引用师逸。用于存儲(chǔ)請求司倚,還有Network, cache, ResponseDelivery
這幾個(gè)對象的作用,我在上面有提到過,不記得了可以向上滑動(dòng)一下回憶一下动知。接下來我們看看NetworkDispatcher
的run方法皿伺。
@Override
public void run() {
//設(shè)置線程的優(yōu)先級為后臺(tái)線程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
//獲得開機(jī)到現(xiàn)在的時(shí)間
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// Take a request from the queue.
//從隊(duì)列中取出一個(gè)請求,如果沒有請求盒粮,就阻塞線程等待請求鸵鸥。
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.判斷請求是否被取消了,取消了的話直接finish丹皱,下一次請求處理
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// Perform the network request. 調(diào)用mNetwork的performRequest妒穴,請求數(shù)據(jù)
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
//對返回的NetworkResponse進(jìn)行檢測,如果notModified并且已經(jīng)將結(jié)果分發(fā)過了(返回給用戶了)摊崭,直接finish
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
//將請求轉(zhuǎn)換成Response對象讼油,parseNetworkResponse時(shí)對應(yīng)的Request的子類實(shí)現(xiàn)的
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.
if (request.shouldCache() && response.cacheEntry != null) {
//如果需要緩存并且cacheEntry不等于空就將本次請求,添加到緩存中
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back. 標(biāo)記已經(jīng)將Response分發(fā)給Delivery(即回調(diào)給Request了)
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
//如果出錯(cuò)了則回調(diào)onError方法
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);
}
}
}
在代碼中呢簸,我已經(jīng)在需要注釋的地方矮台,添加了詳細(xì)的注釋了。所以我們講一下大概的根时,NetworkDispatcher
的run
方法在一個(gè)無限循環(huán)中不斷的從請求隊(duì)列中獲取Request
瘦赫,然后調(diào)用mNetwork.performRequest
請求網(wǎng)絡(luò)數(shù)據(jù)。這里的mNetwork是一個(gè)BasicNetwork對象蛤迎,那么我們就插播一個(gè)BasicNetwork
的源碼分析耸彪,從而看看mNetwork.performRequest(request)
究竟都干了什么。
BasicNetwork
這部分主要是對BasicNetwork
的源碼的解析忘苛。那BasicNetwork
是用來干什么的呢蝉娜,BasicNetwork
在我的理解中就是通過HttpStack
來執(zhí)行一個(gè)Volley request
的。我們挑重點(diǎn)的代碼來講解一下扎唾,BasicNetwork
主要干了些什么召川。
首先我們看BasicNetwork
的定義和構(gòu)造函數(shù):
/**
* A network performing Volley requests over an {@link HttpStack}.
*/
public class BasicNetwork implements Network {
/**
* @param httpStack HTTP stack to be used
*/
public BasicNetwork(HttpStack httpStack) {
// If a pool isn't passed in, then build a small default pool that will give us a lot of
// benefit and not use too much memory.
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}
/**
* @param httpStack HTTP stack to be used
* @param pool a buffer pool that improves GC performance in copy operations
*/
public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
mHttpStack = httpStack;
mPool = pool;
}
讓我一起來看一下,BasicNetwork
繼承自Network
,那么Network
里面定義了什么呢胸遇?
/**
* An interface for performing requests.
*/
public interface Network {
/**
* Performs the specified request.
* @param request Request to process
* @return A {@link NetworkResponse} with data and caching metadata; will never be null
* @throws VolleyError on errors
*/
NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
可以看到Network
只是定義了一個(gè)抽象方法提供給子類實(shí)現(xiàn)荧呐,從方法的注釋來看performRequest
就是執(zhí)行了一個(gè)請求并且返回NetworkResponse
即網(wǎng)絡(luò)請求的響應(yīng),具體怎么請求的由其子類的實(shí)現(xiàn)決定纸镊。好倍阐,從這里我們就可以知道performRequest
是我們請求網(wǎng)絡(luò)最關(guān)鍵的一個(gè)方法。
繼續(xù)看BasicNetwork
的構(gòu)造方法逗威,傳入了兩個(gè)參數(shù)峰搪,第一個(gè)參數(shù)HttpStack
是一個(gè)封裝了網(wǎng)絡(luò)請求的類,其實(shí)BasicNetwork
的網(wǎng)絡(luò)請求的真正的執(zhí)行是調(diào)用了HttpStack
來實(shí)現(xiàn)的凯旭,在上一小節(jié)我們提到過概耻。
第二個(gè)參數(shù)是一個(gè)ByteArrayPool
,這個(gè)類是android提供的用于避免重復(fù)創(chuàng)建byte[]的一個(gè)緩存機(jī)制使套,類似于LruCache。減少堆內(nèi)存的消耗鞠柄,提高性能侦高。
PerformRqeust
接下來就來看看BasicNetwork
的performRequest
方法是如何請求網(wǎng)絡(luò)數(shù)據(jù)并且返回響應(yīng),以及如何處理的厌杜。
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
//獲取開機(jī)到現(xiàn)在的時(shí)間
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
//創(chuàng)建一個(gè)新的空的集合
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
//根據(jù)緩存來設(shè)置請求頭的一些值 request.getCacheEntry() 返回一個(gè)request的緩存實(shí)體
addCacheHeaders(headers, request.getCacheEntry());
//發(fā)送一個(gè)請求并且得到相應(yīng)的HttpResponse
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode(); //獲取返回的狀態(tài)碼
//獲取響應(yīng)的頭部信息
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
//當(dāng)一個(gè)網(wǎng)絡(luò)請求返回的響應(yīng)碼是304的時(shí)候奉呛,說明該url對應(yīng)的資源沒有改變,所以可以直接從緩存中獲取上次緩存的數(shù)據(jù)
Entry entry = request.getCacheEntry();
if (entry == null) {
//如果entry為空的話夯尽,直接將響應(yīng)頭傳入NetworkResponse
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
//當(dāng)響應(yīng)碼是304時(shí)瞧壮,服務(wù)器的響應(yīng)頭里面會(huì)有一些新的信息,所以需要更新緩存中的響應(yīng)頭信息呐萌,比如新的過期時(shí)間等
// 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
entry.responseHeaders.putAll(responseHeaders);
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.
if (httpResponse.getEntity() != null) {
//將HttpEntity轉(zhuǎn)換成對應(yīng)的byte[]數(shù)組
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents = new byte[0];
}
// if the request is slow, log it.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
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;
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());
}
}
}
}
在上面的代碼中我自己添加了比較詳細(xì)的注釋了馁痴。我們兩一起看看谊娇,我覺得比較重要的幾個(gè)方法肺孤。 在我們請求之前我們調(diào)用了addCacheHeaders(headers, request.getCacheEntry());
來給請求頭添加了一些參數(shù)。 跳進(jìn)addCacheHeaders
方法來看看它做了什么!!
private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
// If there's no cache entry, we're done.
if (entry == null) {
return;
}
//初始化"If-None-Match"以及"If-Modified-Since"兩個(gè)變量可以用于之后 判斷是否服務(wù)器上的數(shù)據(jù)是否改變了
if (entry.etag != null) {
headers.put("If-None-Match", entry.etag);
}
if (entry.lastModified > 0) {
Date refTime = new Date(entry.lastModified);
headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
}
}
方法很簡單首先判斷一下緩存是否是空的济欢,空的直接返回赠堵,不是空的話,取出entry.etag
和entry.lastModified
分別賦值給請求頭的If-None-Match
和If-Modified-Since
.為什么要這樣做呢法褥,在網(wǎng)絡(luò)請求的時(shí)候茫叭,一般當(dāng)我們的請求有緩存的時(shí)候并且我們本地緩存過期的時(shí)候,因?yàn)榫彺嫫谶^了半等,并不代表服務(wù)器上面的資源就一定變了揍愁。所以我們需要判斷一下服務(wù)器上面的資源是否變了。那么如何判斷呢杀饵?
這個(gè)時(shí)候就通過在請求中添加上一次緩存響應(yīng)中的響應(yīng)頭的中的If-Modified-Since
和If-None-Match
到請求響應(yīng)頭中莽囤,并傳遞給服務(wù)器。服務(wù)器接收到這個(gè)請求頭的時(shí)候就會(huì)根據(jù)這兩個(gè)變量判斷一下資源是否改變切距。
那么If-Modified-Since
和If-None-Match
分別代表了什么呢朽缎。
Last-Modified
標(biāo)記了資源文件在服務(wù)器的最后修改時(shí)間,當(dāng)客戶端由于緩存過期發(fā)起請求時(shí)谜悟,請求頭要使用If-Modified-Since頭部话肖,它的值就是第一次服務(wù)器返回的Last-Modified。服務(wù)器收到這個(gè)時(shí)間后葡幸,跟當(dāng)前的資源文件最后修改時(shí)間進(jìn)行對比最筒,如果服務(wù)器中資源文件的最后修改時(shí)間新與Last-Modified的值,那么說明資源文件進(jìn)行修改過蔚叨。
Etag
又是什么呢是钥,它是一個(gè)資源文件的標(biāo)示掠归,同樣的當(dāng)客戶端由于緩存過期發(fā)送請求時(shí), 請求頭把上一次服務(wù)器返回的Etag
傳入請求頭悄泥,服務(wù)器接收到之后虏冻,與當(dāng)前資源的Etag
對比,如果不一樣了弹囚,則說明資源改變了厨相。所以通過這兩個(gè)變量就可以判斷服務(wù)器的資源到底有沒有變。
服務(wù)器通過上面兩個(gè)變量判斷資源是否改變了鸥鹉。如果沒有改變則返回304蛮穿,并返回新的響應(yīng)頭里面包含新的過期時(shí)間。如果改變了則返回200響應(yīng)碼.
addCacheHeaders
之后就可以執(zhí)行請求了毁渗,通過httpStack
獲得HttpResponse
践磅,根據(jù)返回響應(yīng)碼,做相應(yīng)的處理灸异。
如果StatusCode
是304府适,則說明服務(wù)器上面的資源沒有改變,則直接從緩存中拿數(shù)據(jù)肺樟,如果entry
為空則直接返回一個(gè)data為null檐春,headers==Response的headers的NetworkResponse
。 但是如果entry
不是空的話么伯,則需要更新entry
的headers
更新過期時(shí)間等疟暖。
如果返回的響應(yīng)碼statusCode > 200 && statusCode < 299
,則 將HttpEntity轉(zhuǎn)換成bytes數(shù)組作為響應(yīng)內(nèi)容,并直接返回NetworkResponse
田柔,否則拋出IOException
然后做相應(yīng)的處理俐巴,最終的結(jié)果要么返回NetworkRespons
,要么拋出VolleyError
類型的異常硬爆,然后調(diào)用出根據(jù)返回值欣舵,來回調(diào)對應(yīng)listener的方法。接下來我就不繼續(xù)講了摆屯。相信聰明的你們都能把剩下的看懂的邻遏。_!虐骑!
接著我們就返回到NetworkDispatcher
的run方法繼續(xù)向下看准验,現(xiàn)在我們知道mNetwork.performRequest(request)
這句話都干了些什么,雖然代碼有點(diǎn)多廷没,但是其實(shí)就是獲得了一個(gè)NetworkResponse糊饱。嚇往下看。為了不用來回拉上去看颠黎,我把剩下的代碼再貼一次另锋。
```
//對返回的NetworkResponse進(jìn)行檢測滞项,如果notModified并且已經(jīng)將結(jié)果分發(fā)過了(返回給用戶了),直接finish
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
//將請求轉(zhuǎn)換成Response對象夭坪,parseNetworkResponse時(shí)對應(yīng)的Request的子類實(shí)現(xiàn)的
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.
if (request.shouldCache() && response.cacheEntry != null) {
//如果需要緩存并且cacheEntry不等于空就將本次請求文判,添加到緩存中
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back. 標(biāo)記已經(jīng)將Response分發(fā)給Delivery(即回調(diào)給Request了)
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
//如果出錯(cuò)了則回調(diào)onError方法
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);
}
```
得到了NetwokResponse
之后,我們需要對返回的請求結(jié)果檢測和處理之后室梅,傳遞給mDelivery
戏仓,最后反饋掉調(diào)用的請求的Listener中。
首先判斷一下networkResponse.notModified
是否為true亡鼠,如果為true赏殃,則說明服務(wù)器上的資源沒有改變,并且request.hasHadResponseDelivered()
這個(gè)請求的請求結(jié)果已經(jīng)分發(fā)過了间涵,那么就將這個(gè)請求finish掉仁热,進(jìn)行下一次請求。上面兩個(gè)條件不同時(shí)符合勾哩,那么調(diào)用request.parseNetworkResponse(networkResponse);
將NetworkResonse
轉(zhuǎn)換成Response
那么parseNetworkResponse
這里方法對Response做了什么呢抗蠢。看看Request的代碼我們可以知道钳幅,parseNetworkResponse
是一個(gè)由子類繼承實(shí)現(xiàn)的一個(gè)方法物蝙,終于我們又可以看回StringRequest
了炎滞,我們看看StringRequest
方法做了些什么敢艰。
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
可以看到它將response轉(zhuǎn)換成了String類型的Response,所以其實(shí)這個(gè)方法就是將NetworkResponse
轉(zhuǎn)換成了對應(yīng)數(shù)據(jù)類型册赛。拿到了Respnse<String>之后我們就可以通過mDelivery.postResponse(request, response);或mDelivery.postError(request, volleyError);
將這個(gè)響應(yīng)钠导,通過StringRequst的deliverResponse方法,然后通過mListener將結(jié)果回調(diào)出去森瘪。 至此一次StringRequst請求我們就分析完了一半了牡属。一起來看看mDelivery
做了什么。
這里mDelivery
是一個(gè)ExecutorDelivery對象扼睬,為什么逮栅?見下:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
并且handler的消息處理都是在主線程哦。因?yàn)?code>new Handler(Looper.getMainLooper()).
那就讓我們一起來看看 ExecutorDelivery這個(gè)類吧窗宇。
首先PO一個(gè)構(gòu)造函數(shù)措伐。
/** Used for posting responses, typically to the main thread. */
private final Executor mResponsePoster;
/**
* Creates a new response delivery interface.
* @param handler {@link Handler} to post responses on
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
用了一個(gè)Executor異步執(zhí)行框架用來封裝handler。然后看看postResponse
和postError
方法军俊。
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
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));
}
可以看到最終都將Response和Reqeust傳遞到了一個(gè)ResponseDeliveryRunable
里面然后執(zhí)行侥加。快馬加鞭粪躬,我們看看ResponseDeliveryRunable的run方法担败。
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
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.
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}
注意在它處理請求之前它又一次判斷了請求是否被取消了昔穴,果然官方的代碼有一些地方就是比較謹(jǐn)慎。記得在NetworkDispatcher
的run方法的時(shí)候就已經(jīng)判斷過一次了提前,這樣多次判斷可以避免在請求返回之前用戶取消了吗货。我們應(yīng)該也學(xué)著這么謹(jǐn)慎,從而避免一些不必要的處理狈网。如果Request沒有被取消掉那么就通過mResponse.isSuccess()
判斷一下請求是否成功卿操,那么它如何判斷請求是否成功的呢。
/**
* Returns whether this response is considered successful.
*/
public boolean isSuccess() {
return error == null;
}
原來他只是簡單的根據(jù)error是否為空來判斷請求成功了沒有孙援,你可以跟蹤一下代碼害淤,就知道當(dāng)請求成功時(shí)候error是null的,失敗的時(shí)候error是一個(gè)VolleyError對象buweinull拓售。如果成功了就調(diào)用mRequest.deliverResponse(mResponse.result);
,然后就成功的請求到數(shù)據(jù)啦窥摄。Request
的deliverResponse
方法如下。
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
至此我們就將請求結(jié)果回調(diào)到一開始new Request的listener里面啦础淤。我們對StringRequest的請求過程的分析崭放,也分析到一半了。為什么是一半呢鸽凶。因?yàn)?code>CacheDispatcher和RequestQueue.add
還沒有分析呢币砂,這也是很重要的知識(shí)點(diǎn)。 由于篇幅原因玻侥,我分兩篇文章來寫决摧。下一篇,馬上就會(huì)發(fā)布的凑兰,敬請期待U谱!
一口氣寫了這么多姑食,累了波岛。閃了。 有問題歡迎留言音半。