一. 重要的类
1.1 RequestQueue
重要变量
//请求等待集合,队列中的请求是重复的,之前已经有一个相同的请求正在执行
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) - 将解析好的响应传递给监听器
每一个Request的缓存key就是它的Url, 如果要创建POST或者PUT请求, 也可以重写以下两个方法:
public byte[] getBody() throws AuthFailureError {}public Map<String, String> 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<Request<?>>类型 -
mNetworkQueue也是PriorityBlockingQueue<Request<?>>类型 -
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<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();
}
}