在Android网络框架-Volley(一) 工作原理分析 中的流程图中我们知道Volley工作是有三个线程的:主线程、缓存线程和网络线程。这些线程的工作是建立在RequestQueue上的,上一篇文章 Android网络框架-Volley(二) RequestQueue源码分析以及建立一个RequestQueue 中我们分析了RequestQueue的源码以及最佳使用方式,这篇文章我们来分析两大管家CacheDispatcher和NetworkDispatcher
当一个request加入到RequestQeue中后,首先是交给CacheDispatcher的,我们分析一个CacheDispatcher的源码
public class CacheDispatcher extends Thread {
//缓存队列
private final BlockingQueue mCacheQueue;
//网络队列
private final BlockingQueue mNetworkQueue;
//cache实例,从cache中取出的数据是一个Entry
private final Cache mCache;
//将数据交给主线程
private final ResponseDelivery mDelivery;
/**
*构造方法
*
* @param cacheQueue 缓存队列
* @param networkQueue 网络队列
* @param cache Cache
* @param delivery 分发者(用于将数据分发给主线程)
*/
public CacheDispatcher(
BlockingQueue cacheQueue, BlockingQueue networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}
/**
* 强制退出
*/
public void quit() {
mQuit = true;
interrupt();
}
//CacheDispatcher是一个线程,在RequestQueue中的start()方法中调用了
//CacheDispatcher的start()方法,之后会执行run()方法
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
//设置一个优先级,这是标准的后台线程优先级,稍微比普通优先级低一些
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 阻塞初始化cache实例
mCache.initialize();
//缓存线程开始循环执行
while (true) {
try {
// 阻塞方法,直到从缓存队列中取出一个request,才会往下执行
final Request request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 如果这个request已经被取消了,那么就不往下执行了
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 从cache中取出数据,取出的数据是一个Entry。里面保存了数据还有一些
//头部信息
Cache.Entry entry = mCache.get(request.getCacheKey());
//如果entry为空,说明缓存中不存在此数据
if (entry == null) {
request.addMarker("cache-miss");
//直接加入到网络队列中,从网络中去获取数据
mNetworkQueue.put(request);
continue;
}
// 如果entry完全过期了,那就加入到网络队列中,重新去获取数据
//判断过期的方法为entry中的ttl<当前系统时间,则为过期
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// cache命中了,把entry中保存的data和头部信息解析出来
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
//没有过期,也不需要刷新,将他交付给主线程显示出来。
if (!entry.refreshNeeded()) {
//由delivery交付给主线程
mDelivery.postResponse(request, response);
} else {
// entry过期但没有完全过期,是需要刷新的状态,那么就先将其
//交付给主线程显示出来,然后再加入到网络线程中,去获取最新数据
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// 将response标记为待完成状态,需要更新
response.intermediate = true;
// 又delivery将数据分发给主线程之后再加入到网络队列中去
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
//加入到网络队列中去
mNetworkQueue.put(request);
} catch (InterruptedException e) {
}
}
});
}
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
}
}
}
有几种情况会吧request加入到网络队列中去获取数据 1.缓存中没有数据,直接到网络中去获取 2.缓存中有数据,但是已经完全过期,直接到网络中去获取 3.缓存中有数据,已经过期但没有完全过期,是一个需要刷新的状态,先将数据交给主线程,再到网络中获取最新数据,然后刷新 接下来我们分析NetworkDispatcher的源码
public class NetworkDispatcher extends Thread {
//需要处理的request的队列
private final BlockingQueue mQueue;
//处理网络数据的接口
private final Network mNetwork;
//cache实例
private final Cache mCache;
//分发者,将获取到的数据分发给主线程
private final ResponseDelivery mDelivery;
/**
* 构造方法
*
* @param queue待处理的request的队列
* @param network 处理请求的网络接口
* @param cache Cache
* @param delivery 分发者
*/
public NetworkDispatcher(BlockingQueue queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
/**
*强制退出
*/
public void quit() {
mQuit = true;
interrupt();
}
//和CacheDispatcher一样,在RequestQueue中调用start()方法后,调用了
//NetworkDispatcher的start()方法,即调用了这个run()方法
@Override
public void run() {
//设置优先级,标准后台线程优先级,略低于普通线程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request request;
while (true) {
try {
// 阻塞方法,从request队列中取出要执行的request
request = mQueue.take();
} catch (InterruptedException e) {
// 判断是否要退出
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
//如果当前request已经取消,则不继续执行
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
// 如果当前手机版本是4.0以上的,则给该request设置一个tag作为标记
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
// 处理网络请求,返回响应数据
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果服务器返回304,或者我们已经请求过一次了,则不再去请求数据
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
//finish掉这个request
request.finish("not-modified");
continue;
}
// 在工作线程中去解析响应数据
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
//如果可以缓存,则缓存到cache中
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 给request一个标记,标记为该request已经被处理完,分发到主线程中
request.markDelivered();
//由delicery将最后的数据交付给主线程
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
mDelivery.postError(request, new VolleyError(e));
}
}
}
}
总结: 1.当一个request加入到requestQueue中,首先会交到CacheDispatcher手中。 1.1.request请求的数据在缓存中 1.1.1.数据已经完全过期,则将request交给NetworkDispatcher去从网络获取数据。执行2 1.1.2.数据已经过期,但没有完全过期,需要刷新,则先将数据交付给主线程去显示,再交给NetworkDispatcher去从网路上获取最新数据,然后再交给主线程去更新。执行2 1.1.3.数据没有过期,则直接从缓存中获取数据后,交付给主线程去显示 1.2.request请求的数据没在缓存中,则交给NetworkDispatcher去从网络上获取。执行2 2.此时NetworkDispatcher拿到了request,到网络中去获取数据 2.1.数据可以写到缓存,则将数据写到缓存,并且交付给主线程去显示 2.2.数据不可以写到缓存,则直接交付给主线程去显示