2017-10-9(Volley使用范例源码分析)
Volley應(yīng)該是比較久遠的產(chǎn)物了。google在2013 IO發(fā)布,但也可以借鑒學(xué)習(xí)畢竟是google工程師的AOSP產(chǎn)物。
下面從范例代碼分析Volley的結(jié)構(gòu)和核心源碼。
RequestQueue mQueue = Volley.newRequestQueue(context);
//url
String url = "http//www.baidu.com";
//返回結(jié)果處理
Response.Listener listener = new Response.Listener<String>() {
public void onResponse(String response) {
Log.e("TAG",response);
}
};
//錯誤返回結(jié)果處理
Response.ErrorListener errorListener = new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
Log.e("TAG",error.getMessage(),error);
}
};
//Request請求
StringRequest stringRequest = new StringRequest(Request.Method.GET,url,
listener, errorListener);
//將請求加入RequestQueue 隊列
mQueue.add(stringRequest);
復(fù)制代碼
實際就三個步驟:
- 創(chuàng)建隊列 RequestQueue
- 創(chuàng)建Request請求
- 將Request請求添加到隊列RequestQueue
創(chuàng)建RequestQueue
RequestQueue mQueue = Volley.newRequestQueue(context);
調(diào)用兩個參數(shù)的構(gòu)造方法:
這個方法有點長截取重要部分
主要是對于9以上版本是創(chuàng)建BasicNetwork對象
然后調(diào)用newRequestQueue(context, network);
看看BasicNetwork對象干什么的?
創(chuàng)建了一個4*1024大小的二進制數(shù)組和將new HurlStack()傳進來。
好,看到這里先把這個類放著,后邊再繼續(xù)看到底做了些什么。
將context和BasicNetwork傳進來
newRequestQueue(context, network);
第一句代碼就不說了,就是在設(shè)備上創(chuàng)建一個文件放緩存;
主要看后面兩句:
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
這里看見創(chuàng)建了一個ExecutorDelivery,和我們十分熟悉的Handler,而這個Handler是主線程的Handler;看看這個ExecutorDelivery是干什么的:
看到這里,ExecutorDelivery主要就是創(chuàng)建一個主線程的Handler,將返回的信息給主線程的Handler處理。Runnable 就是給主線程處理的。
繼續(xù)看剛剛的構(gòu)造方法
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
- cache 就是DiskBasedCache主要就是緩存
- network 就是BasicNetwork主要就是發(fā)送http請求的,但真正實現(xiàn)發(fā)送是HurlStack,后面會帶大家看源碼講解。
- mDispatchers就是創(chuàng)建一個NetworkDispatcher數(shù)組
- mDelivery 剛才已經(jīng)看了代碼就是給主線程Handler處理回調(diào)結(jié)果用的
RequestQueue創(chuàng)建完畢下一步
queue.start()
stop();英文注解寫得很清楚就是確保目前正在執(zhí)行的dispatcher停止(不管是網(wǎng)絡(luò)的還是緩存的)
之后的代碼逐行解釋
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
- mCacheQueue是緩存隊列
- mNetworkQueue是網(wǎng)絡(luò)請求隊列
- mCache剛才說了是DiskBasedCache主要就是緩存
- mDelivery剛才已經(jīng)看了代碼就是給主線程Handler處理回調(diào)結(jié)果用的
這里多了個WaitingRequestManager,看看干什么的:
先放著,構(gòu)造函數(shù)并沒有干什么
mCacheDispatcher.start();
我們看看CacheDispatcher類
繼承Tread,那么start()即是調(diào)用run()
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
//最高優(yōu)先級
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
mCache.initialize();
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
continue;
}
// If it is completely expired, just send it to the network.
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
//發(fā)送請求
mDelivery.postResponse(request, response);
} else {
// Soft-expired cache hit. We can deliver the cached response,
// but we need to also send the request to the network for
// refreshing.
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
mDelivery.postResponse(request, response, new Runnable() {
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
});
} else {
// request has been added to list of waiting requests
// to receive the network response from the first request once it returns.
mDelivery.postResponse(request, response);
}
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
}
}
}
復(fù)制代碼
方法比較長,我說重點:
首先設(shè)置了該線程為最高級別,然后緩存初始化,之后就是一個無限循環(huán)。里面首先在緩存隊列拿出一個request,如果該request是已經(jīng)取消就退出循環(huán)。不是繼續(xù)往下走,從緩存mCache取一個entry,如果是空的話,mWaitingRequestManager.maybeAddToWaitingRequests(request),調(diào)用這句。看看干什么的:
String cacheKey = request.getCacheKey();
// Insert request into stage if there's already a request with the same cache key
// in flight.
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
List<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new ArrayList<Request<?>>();
}
request.addMarker("waiting-for-response");
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.d("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
return true;
} else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
request.setNetworkRequestCompleteListener(this);
if (VolleyLog.DEBUG) {
VolleyLog.d("new request, sending to network %s", cacheKey);
}
return false;
}
}
復(fù)制代碼
由于我們是第一次作請求,基本就沒有緩存,所以是走else部分:
里面只將cacheKey給mWaitingRequests存了起來,然后就是我截圖紅色框部分,最后返回false;
setNetworkRequestCompleteListener實現(xiàn)接口,其實就是這個:
繼續(xù)看回之前的:
因為返回false,即調(diào)用mNetworkQueue.put(request);
網(wǎng)絡(luò)隊列添加了一個request,然后跳出循環(huán);
繼續(xù)看回RequestQueue的start():
剛才走到紅色箭頭,繼續(xù)往下走:
由于mDispathcers的容量設(shè)置了4個,所以循環(huán)4次:
里面就是new一個NetworkDispatcher
- mNetworkQueue是網(wǎng)絡(luò)隊列
- network 就是BasicNetwork主要就是發(fā)送http請求的,但真正實現(xiàn)發(fā)送是HurlStack
- cache 就是DiskBasedCache主要就是緩存
- mDelivery 剛才已經(jīng)看了代碼就是給主線程Handler處理回調(diào)結(jié)果用的
networkDispatcher.start();
看看NetworkDispatcher類:
和CacheDispatcher類一樣,就不多說,直接看run()
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
//獲取系統(tǒng)開機直到現(xiàn)在的時間包含睡眠時間
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// Take a request from the queue.
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.
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
request.notifyListenerResponseNotUsable();
continue;
}
addTrafficStatsTag(request);
// Perform the network request.
//執(zhí)行http請求mNetwork 是BasicNetwork
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 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");
request.notifyListenerResponseNotUsable();
continue;
}
// Parse the response here on the worker thread.
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) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back.
request.markDelivered();
mDelivery.postResponse(request, response);
request.notifyListenerResponseReceived(response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
request.notifyListenerResponseNotUsable();
} 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.notifyListenerResponseNotUsable();
}
}
}
復(fù)制代碼
NetworkDispatcher這個類和CacheDispatcher很相似,不同之處只是CacheDispatcher是通過緩存獲得返回結(jié)果,而NetworkDispatcher是通過發(fā)送網(wǎng)絡(luò)請求獲得。
Process.setThreadPriority(Process.THREADPRIORITYBACKGROUND);
第一步也是設(shè)置該線程為最高級別;然后就是一個無限循環(huán);
request = mQueue.take();
就是從網(wǎng)絡(luò)集合獲取request對象;
這句代碼與CacheDispatcher不同,看到注解啦吧;
這里mNetwork是什么呢?其實就是
BasicNetwork,看看它的performRequest()方法:
* 執(zhí)行Request請求
* */
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
//返回一個開機以來包括睡眠的度過時間
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
List<Header> responseHeaders = Collections.emptyList();
try {
// Gather headers.
Map<String, String> additionalRequestHeaders =
getCacheHeaders(request.getCacheEntry());
//返回http結(jié)果
httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
//狀態(tài)碼
int statusCode = httpResponse.getStatusCode();
//response頭部
responseHeaders = httpResponse.getHeaders();
// Handle cache validation.
/**
* 當(dāng)出現(xiàn)304(服務(wù)端有緩存且有效)會自己構(gòu)建一個Response返回
* */
if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
/**
* 304:
* Not Modified 客戶端有緩沖的文檔并發(fā)出了一個條件性的請求(一般是提供If-Modified-Since頭表示客戶只想比指定日期更新的文檔
* 返回 304 的時候已經(jīng)做了一次數(shù)據(jù)庫查詢,但是可以避免接下來更多的數(shù)據(jù)庫查詢,
* 并且沒有返回頁面內(nèi)容而只是一個 HTTP Header,
* 從而大大的降低帶寬的消耗,對于用戶的感覺也是提高。
* 服務(wù)器會自動完成 Last Modified(緩存文件的) 和 If Modified Since(請求中包含的)
* */
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED, null, true,
SystemClock.elapsedRealtime() - requestStart, responseHeaders);
}
// Combine cached and response headers so the response will be complete.
//entry為Response的body
List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);
return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED, entry.data,
true, SystemClock.elapsedRealtime() - requestStart, combinedHeaders);
}
// Some responses such as 204s do not have content. We must check.
InputStream inputStream = httpResponse.getContent();
if (inputStream != null) {
responseContents =
inputStreamToBytes(inputStream, httpResponse.getContentLength());
} 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, statusCode);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, false,
SystemClock.elapsedRealtime() - requestStart, responseHeaders);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
int statusCode;
if (httpResponse != null) {
statusCode = httpResponse.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, false,
SystemClock.elapsedRealtime() - requestStart, responseHeaders);
if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED ||
statusCode == HttpURLConnection.HTTP_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());
}
}
}
}
復(fù)制代碼
很長一大段,我們看核心的:
一開始想通過request獲取緩存entry通過entry獲取http頭部信息;
然后
httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
mBaseHttpStack又是什么玩意呢?
其實就是之前
所以上邊我已經(jīng)透露過真正執(zhí)行http請求的是HurlStack
這里我不過多截圖了,有興趣可以看看Volley源碼HurlStack的executeRequest方法。其實這里它是通過HttpURLConnection來完成http請求的。相信大家對HttpURLConnection都很熟悉,在沒有使用過任何網(wǎng)絡(luò)請求框架的時候Android不是使用Apache的httpclient就是使用java里面的HttpURLConnection。
我們繼續(xù)BasicNetwork的performRequest:
請求返回獲取狀態(tài)碼,獲取返回的頭部信息
通過狀態(tài)碼判斷如果是304的話:
說明客戶端有緩沖的文檔,文檔信息還是有效的。
如果不是304狀態(tài)碼,
獲取了返回的內(nèi)容
最后返回NetworkResponse對象
繼續(xù)看回NetworkDispatcher:
獲取到返回的NetworkResponse
request.parseNetworkResponse(networkResponse);
這句代碼只是將返回的內(nèi)容通過頭部編碼信息轉(zhuǎn)換一下;
其實這里還有一段代碼就是將返回的信息作緩存,下次如果有同一個請求的時候如果狀態(tài)碼返回304就可以通過緩存獲取不走網(wǎng)絡(luò)請求內(nèi)容了。
核心代碼是
mDelivery.postResponse(request, response);
這段代碼其實就是ExcutorDelivery的postResponse
下面截圖是ResponseDeliveryRunnable的run方法
就是將返回結(jié)果給Request的deliverResponse或deliverError
這里只截了deliverResponse,看看里面其實就是我們范例代碼的listener。
整個Volley的請求流程就是這樣一個流程了?,F(xiàn)在總結(jié)一下:
- Volley實際是通過HttpURLConnection完成網(wǎng)絡(luò)請求的;
- Volley最大的特點是有緩存隊列,先走緩存隊列,緩存隊列沒有信息再走網(wǎng)絡(luò)隊列;
- Volley沒有創(chuàng)建線程池,而是默認(rèn)創(chuàng)建4個Thread執(zhí)行網(wǎng)絡(luò)請求;
- 最后Volley使用了PriorityBlockingQueue這個隊列,該隊列特點線程安全的可以根據(jù)優(yōu)先級別獲取元素,而存放在PriorityBlockingQueue里面的元素需要實現(xiàn)Comparable接口;
最后放一張自己畫的VolleyUML圖,宏觀理解一下Volley的結(jié)構(gòu):
由于只是從范例代碼的角度看源碼,有些類沒有涉及到看,如果有什么問題歡迎指正和學(xué)習(xí),共同交流。
轉(zhuǎn)載于:https://juejin.im/post/5a30aaea6fb9a0450f21ec67
總結(jié)
以上是生活随笔為你收集整理的2017-10-9(Volley使用范例源码分析)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL数据库以及其Python用法
- 下一篇: 代码提示级别设置 inspection