先看UML
可以说RequestQueue是volley最为核心的类了,其他一切都是围绕着该类进行运转的。之前讲述的Volley类,其主要工作就是创建RequestQueue的实例并start()。
用最概括的话来描述RequestQueue就是:汇集所有Request并分发给缓存和网络调度线程。
RequestQueue的工作流程是这样的:
有了上边总体的概述,在逐步看下代码:
1.创建RequestQueue实例后,调用start()方法,此时就创建一个缓存调度线程和默认的四个网络调度线程,并开启线程。
/**
* Starts the dispatchers in this queue.
* 重要方法:
* 1.开启缓存调度线程
* 2.开启网络调度线程
*/
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// 创建一个新的缓存调度线程,并将缓存请求队列和网络请求队列传递进去,还有缓存和响应传递机制
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue,
mCache, mDelivery);
// 启动缓存调度线程
mCacheDispatcher.start();
//开启网络调度线程,默认四个
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(
mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
2.当有新的request后,调用add()方法将request添加到请求队列中,然后判断该request是否需要缓存,如果不需要缓存就添加到网络请求队列中;如果需要缓存,先判断在等待队列中是否存在,如果已经存在则将该请求添加进等待队列,如果等待队列中不存在,则先在等待队列中push一个cachekey,value为空,在添加进缓存队列。(在等待队列中先push一个cachekey,其value为空,表示已经有个request曾要进入等待队列,但是进入了缓存队列,那么后边相同的request就进入到等待队列中吧。)
3.此时request已经明显有了归宿:缓存请求队列、网络请求队列和等待队列
4.而在start()内开启的缓存调度线程和网络调度线程会从缓存请求队列和网络请求队列中取出request进行执行。
/**
* 将一个请求添加到请求队列中
*/
public Request add(Request request) {
request.setRequestQueue(this);
// 添加到当前正在处理的请求队列中
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// Process requests in the order they are added.给请求设置请求序列号
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// to the network. 如果该请求不需要被缓存 那么就直接添加到网络请求队列中
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
//对于需要缓存的请求,需要放入等待队列中
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
// 如果等待队列中包含该请求
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
// 取出该请求的对应key对应的所有请求的队列,这里面存放的都是相同key的请求
Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey);
// 如果为空
if (stagedRequests == null) {
// 就先创建一个集合用于存放请求
stagedRequests = new LinkedList<Request>();
}
// 将该请求添加到集合中
stagedRequests.add(request);
// 重新存放到等待队列中
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog
.v("Request for cacheKey=%s is in flight, putting on hold.",
cacheKey);
}
} else {
// 如果等待队列中不存在,那么就将key存入,等以后如果有相同的request那么可以随时存放到等待队列中,这里就是为什么要判断if
mWaitingRequests.put(cacheKey, null);
// 既然已经到这里了(上面的 if (!request.shouldCache())
// ),那么该请求肯定是需要缓存的,因此将其添加到缓存中
mCacheQueue.add(request);
}
return request;
}
}
5.可以根据tag来关闭一个或多个request,需要调用cancelAll()方法。
/**
* 取消当前请求集合中所有符合条件的请求。 filter 参数表示可以按照自定义的过滤器过滤需要取消的请求。 tag
* 表示按照Request.setTag设置好的 tag 取消请求,比如同属于某个 Activity 的
*/
public void cancelAll(RequestFilter filter) {
// 遍历当前所有正在请求还没有完成的请求
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
// 如果请求的tag与给定的tag一致,返回true
if (filter.apply(request)) {
// 那么取消当前请求
request.cancel();
}
}
}
}
/**
* 取消当前请求集合中所有符合条件的请求。 filter 参数表示可以按照自定义的过滤器过滤需要取消的请求。 tag
* 表示按照Request.setTag设置好的 tag 取消请求,比如同属于某个 Activity 的
*/
public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException(
"Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> request) {
// 实现过滤接口,该方法返回值,表示给定tag是否与请求的tag一致,如果一致返回true
return request.getTag() == tag;
}
});
}
6.当一个request执行完毕后,需要调用RequestQueue的finish()方法,在该方法内,先将request从请求队列中移除,然后再从等待队列中获取所有相同的request并存放到缓存请求队列中。
/**
* 告诉请求队列,这个请求已经完成了。这个方法是在request内的finish方法调用的(注意请求完成(finish)和请求取消(cancel)
* 是不同的概念,完成表示已经发送完网络请求并且拿回了数据,取消表示正在发送网络请求或拿数据但是还没完成)
*
* (1). 首先从正在进行中请求集合mCurrentRequests中移除该请求。 (2).
* 然后查找请求等待集合mWaitingRequests中是否存在等待的请求
* ,如果存在,则将等待队列移除,并将等待队列所有的请求添加到缓存请求队列中,让缓存请求处理线程CacheDispatcher自动处理。
*/
void finish(Request request) {
synchronized (mCurrentRequests) {
// 在当前对队列中将该请求给移除
mCurrentRequests.remove(request);
}
if (request.shouldCache()) {
// 如果该请求是可以被缓存的
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
// 从等待队列中获取该key对应的所有的请求
Queue<Request> waitingRequests = mWaitingRequests
.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog
.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
// 'request'.为什么要添加到缓存队列中,这是因为当一个请求已经完成,并且获取到了响应,就会将该请求标记为已完成也就是说需要再当前的请求队列中finish掉该请求,而在等待队列中存在的那些相同url的请求,就不需要再次发送网络请求了
// ,而认为是可以获取之前的请求的数据,因此需要将这些相同的请求从等待队列中移除,添加到缓存队列中,这样这些请求就会直接从缓存队列中获取数据,而不用再去发送网络请求了
mCacheQueue.addAll(waitingRequests);
}
}
}
}