Volley应该是比较久远的产物了。google在2013 IO发布,但也可以借鉴学习毕竟是google工程师的AOSP产物。
下面从范例代码分析Volley的结构和核心源码。
//创建RequestQueue 队列
RequestQueue mQueue = Volley.newRequestQueue(context);
//url
String url = "http//www.baidu.com";
//返回结果处理
Response.Listener listener = new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.e("TAG",response);
}
};
//错误返回结果处理
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
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);
实际就三个步骤:
- 创建队列 RequestQueue
- 创建Request请求
- 将Request请求添加到队列RequestQueue
创建RequestQueue
Paste_Image.pngRequestQueue mQueue = Volley.newRequestQueue(context);
调用两个参数的构造方法:
Paste_Image.png Paste_Image.png这个方法有点长截取重要部分
主要是对于9以上版本是创建BasicNetwork对象
然后调用newRequestQueue(context, network);
看看BasicNetwork对象干什么的?
创建了一个4*1024大小的二进制数组和将new HurlStack()传进来。
好,看到这里先把这个类放着,后边再继续看到底做了些什么。
将context和BasicNetwork传进来
Paste_Image.pngnewRequestQueue(context, network);
第一句代码就不说了,就是在设备上创建一个文件放缓存;
主要看后面两句:
Paste_Image.png Paste_Image.pngRequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
这里看见创建了一个ExecutorDelivery,和我们十分熟悉的Handler,而这个Handler是主线程的Handler;看看这个ExecutorDelivery是干什么的:
Paste_Image.png看到这里,ExecutorDelivery主要就是创建一个主线程的Handler,将返回的信息给主线程的Handler处理。Runnable 就是给主线程处理的。
继续看刚刚的构造方法
Paste_Image.pngpublic RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
- cache 就是DiskBasedCache主要就是缓存
- network 就是BasicNetwork主要就是发送http请求的,但真正实现发送是HurlStack,后面会带大家看源码讲解。
- mDispatchers就是创建一个NetworkDispatcher数组
- mDelivery 刚才已经看了代码就是给主线程Handler处理回调结果用的
RequestQueue创建完毕下一步
Paste_Image.pngqueue.start()
stop();英文注解写得很清楚就是确保目前正在执行的dispatcher停止(不管是网络的还是缓存的)
Paste_Image.png之后的代码逐行解释
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
- mCacheQueue是缓存队列
- mNetworkQueue是网络请求队列
- mCache刚才说了是DiskBasedCache主要就是缓存
- mDelivery刚才已经看了代码就是给主线程Handler处理回调结果用的
这里多了个WaitingRequestManager,看看干什么的:
Paste_Image.png先放着,构造函数并没有干什么
Paste_Image.pngmCacheDispatcher.start();
我们看看CacheDispatcher类
继承Tread,那么start()即是调用run()
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
//最高优先级
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.
//发送请求
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() {
@Override
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;
}
}
}
}
方法比较长,我说重点:
首先设置了该线程为最高级别,然后缓存初始化,之后就是一个无限循环。里面首先在缓存队列拿出一个request,如果该request是已经取消就退出循环。不是继续往下走,从缓存mCache取一个entry,如果是空的话,mWaitingRequestManager.maybeAddToWaitingRequests(request),调用这句。看看干什么的:
private synchronized boolean maybeAddToWaitingRequests(Request<?> request) {
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;
}
}
Paste_Image.png
由于我们是第一次作请求,基本就没有缓存,所以是走else部分:
Paste_Image.png里面只将cacheKey给mWaitingRequests存了起来,然后就是我截图红色框部分,最后返回false;
setNetworkRequestCompleteListener实现接口,其实就是这个:
继续看回之前的:
Paste_Image.png因为返回false,即调用mNetworkQueue.put(request);
网络队列添加了一个request,然后跳出循环;
继续看回RequestQueue的start():
Paste_Image.png刚才走到红色箭头,继续往下走:
由于mDispathcers的容量设置了4个,所以循环4次:
里面就是new一个NetworkDispatcher
- mNetworkQueue是网络队列
- network 就是BasicNetwork主要就是发送http请求的,但真正实现发送是HurlStack
- cache 就是DiskBasedCache主要就是缓存
- mDelivery 刚才已经看了代码就是给主线程Handler处理回调结果用的
Paste_Image.pngnetworkDispatcher.start();
看看NetworkDispatcher类:
和CacheDispatcher类一样,就不多说,直接看run()
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
//获取系统开机直到现在的时间包含睡眠时间
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.
//执行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();
}
}
}
NetworkDispatcher这个类和CacheDispatcher很相似,不同之处只是CacheDispatcher是通过缓存获得返回结果,而NetworkDispatcher是通过发送网络请求获得。
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
第一步也是设置该线程为最高级别;然后就是一个无限循环;
request = mQueue.take();
就是从网络集合获取request对象;
Paste_Image.png这句代码与CacheDispatcher不同,看到注解啦吧;
这里mNetwork是什么呢?其实就是
Paste_Image.pngBasicNetwork,看看它的performRequest()方法:
/**
* 执行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结果
httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
//状态码
int statusCode = httpResponse.getStatusCode();
//response头部
responseHeaders = httpResponse.getHeaders();
// Handle cache validation.
/**
* 当出现304(服务端有缓存且有效)会自己构建一个Response返回
* */
if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
/**
* 304:
* Not Modified 客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档
* 返回 304 的时候已经做了一次数据库查询,但是可以避免接下来更多的数据库查询,
* 并且没有返回页面内容而只是一个 HTTP Header,
* 从而大大的降低带宽的消耗,对于用户的感觉也是提高。
* 服务器会自动完成 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());
}
}
}
}
很长一大段,我们看核心的:
Paste_Image.png一开始想通过request获取缓存entry通过entry获取http头部信息;
然后
httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
mBaseHttpStack又是什么玩意呢?
其实就是之前
所以上边我已经透露过真正执行http请求的是HurlStack
这里我不过多截图了,有兴趣可以看看Volley源码HurlStack的executeRequest方法。其实这里它是通过HttpURLConnection来完成http请求的。相信大家对HttpURLConnection都很熟悉,在没有使用过任何网络请求框架的时候Android不是使用Apache的httpclient就是使用java里面的HttpURLConnection。
我们继续BasicNetwork的performRequest:
Paste_Image.png请求返回获取状态码,获取返回的头部信息
通过状态码判断如果是304的话:
说明客户端有缓冲的文档,文档信息还是有效的。
如果不是304状态码,
Paste_Image.png获取了返回的内容
最后返回NetworkResponse对象
继续看回NetworkDispatcher:
获取到返回的NetworkResponse
request.parseNetworkResponse(networkResponse);
这句代码只是将返回的内容通过头部编码信息转换一下;
其实这里还有一段代码就是将返回的信息作缓存,下次如果有同一个请求的时候如果状态码返回304就可以通过缓存获取不走网络请求内容了。
核心代码是
Paste_Image.pngmDelivery.postResponse(request, response);
这段代码其实就是ExcutorDelivery的postResponse
下面截图是ResponseDeliveryRunnable的run方法
Paste_Image.png就是将返回结果给Request的deliverResponse或deliverError
Paste_Image.png这里只截了deliverResponse,看看里面其实就是我们范例代码的listener。
整个Volley的请求流程就是这样一个流程了。现在总结一下:
- Volley实际是通过HttpURLConnection完成网络请求的;
- Volley最大的特点是有缓存队列,先走缓存队列,缓存队列没有信息再走网络队列;
- Volley没有创建线程池,而是默认创建4个Thread执行网络请求;
- 最后Volley使用了PriorityBlockingQueue这个队列,该队列特点线程安全的可以根据优先级别获取元素,而存放在PriorityBlockingQueue里面的元素需要实现Comparable接口;
最后放一张自己画的VolleyUML图,宏观理解一下Volley的结构:
VolleyUML.png由于只是从范例代码的角度看源码,有些类没有涉及到看,如果有什么问题欢迎指正和学习,共同交流。
网友评论