201709021工作日记--Volley源码解读(四)

时间:2021-12-15 07:02:05

接着volley源码(三)继续,本来是准备写在(三)后面的,但是博客园太垃圾了,写了半天居然没保存上,要不是公司这个博客还没被限制登陆,鬼才用这个。。。真是垃圾

继续解读RequestQueue的源码,Volley 的入口是创建一个 RequestQueue 队列,然后开启一个缓存线程和一组网络线程,等待用户 add 新的 request。那我们现在看一下 add 方法里面,RequestQueue 做了哪些事情。

/**
* 添加一个请求到这个消息队列中去
* Adds a Request to the dispatch queue.
* @param request The request to service
* @return The passed-in request
*/
public <T> Request<T> add(Request<T> request) {
// Tag the request as belonging to this queue and add it to the set of current requests.
// 将请求标记为属于此队列,并将其添加到当前请求队列中去
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
} // Process requests in the order they are added.
// 按照添加的顺序处理请求
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue"); // If the request is uncacheable, skip the cache queue and go straight to the network.
// 如果该请求无法缓存进入缓存队列,则将请求添加到网络缓存队列中
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
} // Insert request into stage if there's already a request with the same cache key in flight.
// 如果此前有相同的请求还没有返回结果的,就将此请求加入mWaiting队列中
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}

下面是add()方法的流程图

201709021工作日记--Volley源码解读(四)

通过源码和流程图就可以很好理解add()方法的作用了,下面我们来看一个更重要的方法start(),这个方法是承上启下的方法,增加完队列请求后需要处理这些请求消息,将这些消息分发到缓存消息调度线程和网络调度线程中去处理。代码如下:

/**
* 开启队列中的线程调度者
* Starts the dispatchers in this queue.
*/
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it. 创建缓存调度线程
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start(); // Create network dispatchers (and corresponding threads) up to the pool size.
// 创建并开启网络调度线程
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}

开启了缓存消息调度线程和网络消息调度线程之后,我们来看看CacheDispatcher这个类干了些啥:

/**
* 内部就是阻塞队列的使用。
* 提供用于在请求队列上执行缓存分流的线程。
* Provides a thread for performing cache triage on a queue of requests.
*
* 将请求添加到指定的缓存队列中去,
* 任何可交付的响应通过{@link ResponseDelivery}发回给呼叫者,
* 缓存未命中,或者处于刷新队列中的,则指派给网络分发器
*
* Requests added to the specified cache queue are resolved from cache.
* Any deliverable response is posted back to the caller via a
* {@link ResponseDelivery}. Cache misses and responses that require
* refresh are enqueued on the specified network queue for processing
* by a {@link NetworkDispatcher}.
*/
public class CacheDispatcher extends Thread { private static final boolean DEBUG = VolleyLog.DEBUG; /**
* The queue of requests coming in for triage.
* 请求队列的分流
* */
private final BlockingQueue<Request<?>> mCacheQueue; /**
* 将要被分发到网络请求队列中的请求
* The queue of requests going out to the network.
* */
private final BlockingQueue<Request<?>> mNetworkQueue; /** The cache to read from. */
private final Cache mCache; /**
* 发布响应消息
* For posting responses.
* */
private final ResponseDelivery mDelivery; /**
* 阻塞队列取消等待的标志信息
* Used for telling us to die.
* */
private volatile boolean mQuit = false; /**
* 创建一个缓存触发的调度线程,需要调用start()方法触发开启这个线程
* Creates a new cache triage dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*
* @param cacheQueue Queue of incoming requests for triage
* @param networkQueue Queue to post requests that require network to
* @param cache Cache interface to use for resolution
* @param delivery Delivery interface to use for posting responses
*/
public CacheDispatcher(
BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
} /**
* 强制停止线程的请求操作。因为这个是阻塞消息队列,防止有消息一直占用资源而无法退出
* 设置标志位并且利用消息中断机制interrupt().
* Forces this dispatcher to quit immediately. If any requests are still in
* the queue, they are not guaranteed to be processed.
*/
public void quit() {
mQuit = true;
interrupt();
} @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
// 从消息缓存的阻塞消息队列中获取到的请求消息,并且标记了下
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.
//如果缓存的响应为空,则将请求加入网络队列中
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);
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; // 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) {
// Not much we can do about this.
}
}
});
} } catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
// 可以将阻塞队列终止掉,以防止浪费更过的系统资源
if (mQuit) {
return;
}
}
}
}
}

首先需要提的是这两个阻塞队列的使用,详细请看前面几篇阻塞队列,防止了线程长时间占用资源而不释放锁的情况,可以设置超时停止线程的标志位private volatile boolean mQuit = false;并且捕获异常InterruptedException()。

其次,从缓存队列取出请求,判断是否请求是否被取消了,如果没有则判断该请求是否有缓存的响应,如果有缓存的响应并且没有过期则对缓存响应进行解析并回调给主线程;如果没有缓存的响应,则将请求加入网络调度线程。

下面是这个类的主要流程图:

201709021工作日记--Volley源码解读(四)