一. 重要的类
1.1 RequestQueue
重要变量
//请求等待集合,队列中的请求是重复的,之前已经有一个相同的请求正在执行
private final Map>> mWaitingRequests =
new HashMap<>();
private final Set> mCurrentRequests = new HashSet>();
//缓存队列
private final PriorityBlockingQueue> mCacheQueue =
new PriorityBlockingQueue<>();
//网络请求队列
private final PriorityBlockingQueue> 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
parseNetworkResponse(NetworkResponse response) - 将http请求返回的响应解析成合适的类型 - abstract protected void deliverResponse(T response) - 将解析好的响应传递给监听器
每一个Request
的缓存key就是它的Url
, 如果要创建POST
或者PUT
请求, 也可以重写以下两个方法:
public byte[] getBody() throws AuthFailureError {}
public Map
getParams() throws AuthFailureError
getBody()
方法默认也是通过getParams
方法来创建Body内容,而getParams
方法默认实现是直接返回null,所以一般构建POST
或PUT
请求应当重写这两个方法中的一个, 一般直接重写getParams
即可
子类
-
StringRequest
, 返回值为String类型 -
JsonRequest
, 代表了Body为json的请求, 本身也是一个抽象类 -
JsonObjectRequest
, 继承了JsonRequest
, 将返回值解析为JSONObject
-
JsonArrayRequest
, 继承了JsonRequest
, 将返回值解析为JsonArrayRequest
-
ImageRequest
, 图片请求,将返回值解析成为Bitmap
1.3 HttpStack
HttpStack
是一个接口,就定义了一个执行方网络请求的方法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
来真正执行网络请求, 之后判断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) {
//创建缓存目录,默认在/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);
//创建请求队列, 默认使用ExecutorDelivery
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
//启动CacheDispatcher和NetworkDispatcher线程,开始接收请求,[2]
queue.start();
return queue;
}
- 创建缓存目录, 默认为
/data/data/pkg_name/files/cache/volley
- 创建对应的
HttpStack
- 创建
BasciNetwork
- 创建
DiskBasedCache
- 创建
RequestQueue
, 并启动
2.2 RequestQueue.start
public void start() {
//停止所有缓存线程和网络请求线程,
stop();
//创建缓存线程,并启动线程. [3]
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
//创建网络请求线程数组中所有的网络请求线程,并启动, [4]
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
-
mCacheQueue
是PriorityBlockingQueue
类型> -
mNetworkQueue
也是PriorityBlockingQueue
类型> -
CacheDispatcher
和NetworkDispatcher
都是继承自Thread
即一个线程类
2.3 CacheDispatcher.run
@Override
public void run() {
//设置缓存线程的优先级
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;
}
}
}
}
- 初始化缓存,获取缓存内容
- 开启无限循环,从
PriorityBlockingQueue
中取出请求,查看请求是否有缓存(请求的key就是url),如果队列中没有请求,则线程阻塞 - 如果没有缓存命中,则将请求放入网络队列,去执行网络请求
- 如果有缓存命中,检查缓存是否失效,如果失效也放入网络队列,去执行网络请求
- 如果缓存命中且有效,调用
Request.parseNetworkResponse
获取相应的Response - 判断缓存需不要刷新,如果不需要刷新,则直接调用
ExceutorDelivery.postResponse
传递Response;如果缓存还需要刷新,则还是将请求放入网络请求队列,去执行网络请求获取最新结果
2.3.1 DiskBasedCache.initialize
@Override
public synchronized void initialize() {
/* 如果缓存目录不存在,则创建缓存目录 */
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) {
/* 如果发生异常,证明该文件不符合Volley的缓存格式
* 删除该文件*/
if (file != null) {
file.delete();
}
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException ignored) { }
}
}
}
2.4 NetworkDispatcher.run
@Override
public void run() {
//设置该网络线程的优先级
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);
/* 执行网络请求, 并获取对应的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) {
... //分发VolleyError
} catch (Exception e) {
... //分发VolleyError
}
}
}
- 从网络请求队列中取出请求,如果没有请求则线程会一直阻塞
- 调用
BasicNetwork.performRequest
执行网络请求,并获取相应的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 responseHeaders = Collections.emptyMap();
try {
// Gather headers.
Map 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 additionalHeaders) throws IOException, AuthFailureError {
//设置Header
String url = request.getUrl();
HashMap 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> 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();
}
}