一. 重要的類
1.1 RequestQueue
重要變量
//請求等待集合,隊列中的請求是重復的娜汁,之前已經有一個相同的請求正在執(zhí)行
private final Map<String, Queue<Request<?>>> mWaitingRequests =
new HashMap<>();
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
//緩存隊列
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<>();
//網絡請求隊列
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<>();
//默認使用的是DiskBasedCache
private final Cache mCache;
/** 默認使用BasicNetwork */
private final Network mNetwork;
/** 默認使用ExecutorDelivery */
private final ResponseDelivery mDelivery;
//網絡請求線程數組
private final NetworkDispatcher[] mDispatchers;
//緩存線程
private CacheDispatcher mCacheDispatcher;
重要方法
-
add()
, 可以將請求放入隊列中 -
start()
,RequestQueue
還負責啟動cache線程和network線程 -
stop()
, 停止緩存線程和網絡線程 -
finish()
, 請求結束后的回調
1.2 Request
抽象了Http請求饿自, Request
本身是一個抽象類撩银,繼承它的子類必須要實現兩個抽象方法:
- abstract protected Response<T> parseNetworkResponse(NetworkResponse response) - 將http請求返回的響應解析成合適的類型
- abstract protected void deliverResponse(T response) - 將解析好的響應傳遞給監(jiān)聽器
每一個Request
的緩存key就是它的Url
, 如果要創(chuàng)建POST
或者PUT
請求滥沫, 也可以重寫以下兩個方法:
public byte[] getBody() throws AuthFailureError {}
public Map<String, String> getParams() throws AuthFailureError
getBody()
方法默認也是通過getParams
方法來創(chuàng)建Body內容尺棋,而getParams
方法默認實現是直接返回null缸夹,所以一般構建POST
或PUT
請求應當重寫這兩個方法中的一個, 一般直接重寫getParams
即可
子類
-
StringRequest
, 返回值為String類型 -
JsonRequest
, 代表了Body為json的請求, 本身也是一個抽象類 -
JsonObjectRequest
, 繼承了JsonRequest
匀油, 將返回值解析為JSONObject
-
JsonArrayRequest
, 繼承了JsonRequest
, 將返回值解析為JsonArrayRequest
-
ImageRequest
, 圖片請求,將返回值解析成為Bitmap
1.3 HttpStack
HttpStack
是一個接口腥椒,就定義了一個執(zhí)行方網絡請求的方法performRequest
, 它有兩個實現類HurlStack
和HttpClientStack
HurlStack
HurlStack
的performRequest
主要是用HttpUrlConnection
來實現網絡請求阿宅,這也是Android官方目前推薦使用的方式
HttpClientStack
HttpClientStack
是使用Apache的HttpClient
來進行網絡請求,這一方式目前目前不被Android官方推薦笼蛛,HttpClient
也已從Android源碼中移除
1.4 BasicNetwork
BasicNetwork
實現了Network
接口洒放,其內部含有HttpStack
引用,其performRequest
主要工作是解析請求的Header
滨砍,調用HttpStack.performRequest
來真正執(zhí)行網絡請求往湿, 之后判斷http響應碼(主要是判斷304,跟緩存相關)惋戏,生成NetworkResponse
1.5 DiskBasedCache
磁盤緩存類领追,實現了Cache
接口
重要方法
-
public synchronized void initialize()
,初始化緩存,掃面緩存目錄(默認為/data/data/pkg_name/files/cache/volley
)得到緩存數據生成緩存header并放入內存 -
public synchronized Entry get(String key)
, 從緩存中獲取緩存header,然后讀取緩存文件 -
public synchronized void put(String key, Entry entry)
, 先檢查如果給當前Entry分配空間以后响逢, 緩存是否會滿绒窑,如果會滿則遍歷并逐個刪除緩存,直到如果為當前Entry分配空間以后舔亭,緩存量小于最大緩存量的0.9些膨, 然后再新建緩存文件
二. 基本流程
2.1 Volley.newRequestQueue
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
//創(chuàng)建緩存目錄蟀俊,默認在/data/data/pkg_name/files/cache/volley
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
//使用HttpUrlConnection的方式進行網絡請求
stack = new HurlStack();
} else {
//使用HttpClient進行http請求
...
}
}
//使用默認的BasicNetwork
Network network = new BasicNetwork(stack);
//創(chuàng)建請求隊列, 默認使用ExecutorDelivery
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
//啟動CacheDispatcher和NetworkDispatcher線程,開始接收請求,[2]
queue.start();
return queue;
}
- 創(chuàng)建緩存目錄, 默認為
/data/data/pkg_name/files/cache/volley
- 創(chuàng)建對應的
HttpStack
- 創(chuàng)建
BasciNetwork
- 創(chuàng)建
DiskBasedCache
- 創(chuàng)建
RequestQueue
, 并啟動
2.2 RequestQueue.start
public void start() {
//停止所有緩存線程和網絡請求線程,
stop();
//創(chuàng)建緩存線程订雾,并啟動線程. [3]
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
//創(chuàng)建網絡請求線程數組中所有的網絡請求線程肢预,并啟動, [4]
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
-
mCacheQueue
是PriorityBlockingQueue<Request<?>>
類型 -
mNetworkQueue
也是PriorityBlockingQueue<Request<?>>
類型 -
CacheDispatcher
和NetworkDispatcher
都是繼承自Thread
即一個線程類
2.3 CacheDispatcher.run
@Override
public void run() {
//設置緩存線程的優(yōu)先級
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 初始化DiskBasedCache, [2.3.1]
mCache.initialize();
while (true) {
try {
/* 從緩存隊列中讀取Request, 如果隊列中沒有請求,則線程阻塞*/
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
/* 如果請求已經被取消葬燎,則直接結束當前請求, 從當前請求集合以及等待請求列表中刪除*/
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
/* 判斷當前請求有沒有緩存误甚, request的cacheKey就是url*/
Cache.Entry entry = mCache.get(request.getCacheKey());
/* 沒有緩存結果缚甩,則直接交友網絡請求線程*/
if (entry == null) {
request.addMarker("cache-miss");
mNetworkQueue.put(request);
continue;
}
/* 如果有緩存結果谱净,但緩存結果已經失效,同樣交由網絡線程*/
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
request.addMarker("cache-hit");
/* 緩存命中擅威,則調用Request的parseNetworkResponse獲取相應的Response*/
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
/* 如果緩存不需要刷新壕探,則直接傳遞結果*/
mDelivery.postResponse(request, response);
} else {
/* 如果緩存還需要刷新,傳遞響應結果郊丛,將將請求交由網絡線程進行新鮮度驗證*/
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
/* 向網絡請求隊列中添加請求*/
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
}
}
}
- 初始化緩存李请,獲取緩存內容
- 開啟無限循環(huán),從
PriorityBlockingQueue
中取出請求厉熟,查看請求是否有緩存(請求的key就是url)导盅,如果隊列中沒有請求,則線程阻塞 - 如果沒有緩存命中揍瑟,則將請求放入網絡隊列白翻,去執(zhí)行網絡請求
- 如果有緩存命中,檢查緩存是否失效绢片,如果失效也放入網絡隊列滤馍,去執(zhí)行網絡請求
- 如果緩存命中且有效,調用
Request.parseNetworkResponse
獲取相應的Response - 判斷緩存需不要刷新底循,如果不需要刷新巢株,則直接調用
ExceutorDelivery.postResponse
傳遞Response;如果緩存還需要刷新,則還是將請求放入網絡請求隊列熙涤,去執(zhí)行網絡請求獲取最新結果
2.3.1 DiskBasedCache.initialize
@Override
public synchronized void initialize() {
/* 如果緩存目錄不存在阁苞,則創(chuàng)建緩存目錄 */
if (!mRootDirectory.exists()) {
if (!mRootDirectory.mkdirs()) {
VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
}
return;
}
/* 獲取緩存目錄下所有文件*/
File[] files = mRootDirectory.listFiles();
if (files == null) {
return;
}
/* 遍歷多有緩存文件,如果能夠正常讀取祠挫,則為每個緩存文件生成一個CacheHeader
* 并放入集合中*/
for (File file : files) {
BufferedInputStream fis = null;
try {
fis = new BufferedInputStream(new FileInputStream(file));
CacheHeader entry = CacheHeader.readHeader(fis);
entry.size = file.length();
putEntry(entry.key, entry);
} catch (IOException e) {
/* 如果發(fā)生異常猬错,證明該文件不符合Volley的緩存格式
* 刪除該文件*/
if (file != null) {
file.delete();
}
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException ignored) { }
}
}
}
2.4 NetworkDispatcher.run
@Override
public void run() {
//設置該網絡線程的優(yōu)先級
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// 從網絡請求隊列中獲取請求
request = mQueue.take();
} catch (InterruptedException e) {
...
continue;
}
try {
...
/* 如果請求已經被取消,則直接結束當前請求, 從當前請求集合以及等待請求列表中刪除*/
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
/* 設置網絡流量標識*/
addTrafficStatsTag(request);
/* 執(zhí)行網絡請求, 并獲取對應的Response [2.4.1]*/
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
/* 如果服務端返回304同時我們已經傳輸了Response, 則結束該Request避免重復傳輸Response*/
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
/* 調用Request的parseNetworkResponse解析結果*/
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
/* 如果Request允許緩存且緩存實體不為空茸歧,則調用DiskBasedCache進行緩存 */
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
/* 標記Request已傳輸結果 */
request.markDelivered();
/* ExecutorDelivery 傳輸結果 */
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
... //分發(fā)VolleyError
} catch (Exception e) {
... //分發(fā)VolleyError
}
}
}
- 從網絡請求隊列中取出請求倦炒,如果沒有請求則線程會一直阻塞
- 調用
BasicNetwork.performRequest
執(zhí)行網絡請求,并獲取相應的Response - 如果Request允許緩存且緩存實體不為空软瞎,則將Request放入緩存
- 調用
ExecutorDelivery.postResponse
傳輸結果
2.4.1 BasicNetwork.performRequest
@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 {
// Gather headers.
Map<String, String> headers = new HashMap<>();
//如果請求中包含緩存字段逢唤,存儲進headers
addCacheHeaders(headers, request.getCacheEntry());
//調用HttpStack.performRequest, 一般情況下拉讯,HttpStack都會是HurlStack
//[2.4.2]
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
//解析Header
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// 返回碼是304
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
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) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents = new byte[0];
}
...
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
//[2.4.3]
attempRetryonExcption("socket", request, new TimeoutError());
} catch .... {//其他excption
.... //基本都是attempRetryonExcption
}
}
}
2.4.2 HurlStack.performRequest
由于目前Android平臺基本都是使用HttpUrlConnection
, 所以這里就默認使用HurlStack
, 不考慮HttpClientStack
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
//設置Header
String url = request.getUrl();
HashMap<String, String> map = new HashMap<>();
map.putAll(request.getHeaders());
//additionalHeader一般都是用來緩存的Header
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
//建立Http連接[2.4.2.1]
HttpURLConnection connection = openConnection(parsedUrl, request);
//設置請求頭
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
//設置請求方法,請求參數等
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
...
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
2.4.2.1 HurlStack.openConnection
private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
HttpURLConnection connection = createConnection(url);
int timeoutMs = request.getTimeoutMs();
connection.setConnectTimeout(timeoutMs);
connection.setReadTimeout(timeoutMs);
connection.setUseCaches(false);
connection.setDoInput(true);
// use caller-provided custom SslSocketFactory, if any, for HTTPS
// 如果scheme是https鳖藕,設置SSLSocketFactory
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
}
return connection;
}
2.4.3 BasicNetwork.attemptRetryOnException
private static void attemptRetryOnException(String logPrefix, Request<?> request,
VolleyError exception) throws VolleyError {
//默認是DefaultRetryPolicy
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs();
try {
//DefaultRetryPolicy中的retry主要就是判斷重試次數是否已經到達上限
retryPolicy.retry(exception);
} catch (VolleyError e) {
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
throw e;
}
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
2.5. ExecutorDelivery.postResponse
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
//向線程池中提交任務
//[2.5.1]
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
2.5.1 ResponseDeliveryRunnable.run()
@Override
public void run() {
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
//如果成功會回調Response.Listener的onResponse
mRequest.deliverResponse(mResponse.result);
} else {
//如果失敗會回調Response.ErrorListener的onErrorResponse
mRequest.deliverError(mResponse.error);
}
if (mResponse.intermediate) {
//如果這是一個intermediate Response魔慷,添加Marker
mRequest.addMarker("intermediate-response");
} else {
//結束當前請求
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}