在 [Android]Volley源码分析(一)概述中,我们看到了Volley通过调研RequestQueue的start()方法启动了一个请求队列,下面我们通过代码看看具体是如何实现的:
/**
* Starts the dispatchers in this queue.
*/
public void start() {
//首先取消当前所有运行的请求分发器dispatcher
stop();
// 创建一个缓存分发器并启动
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();
}
}
通过代码我们发现请求队列启动时,启动了一个缓存分发器(Dispatcher)和4个网络分发器(Dispatcher)。这些分发器(Dispatcher)每个都维护了一个阻塞消息队列(BlockingQueue),阻塞消息队列(BlockingQueue)是为了实现“生产者-消费者”这种模型而设计的队列:当有消息加入队列时,则会一次取出消息执行,而当队列里没有消息时,则会一直等待。在androidsdk中阻塞消息队列(BlockingQueue)有三种实现方式:LinkedBlockingQueue,ArrayBlockingQueue和PriorityBlockingQueue。Volley采用的是PriorityBlockingQueue,这是一种基于消息优先级的高低来执行队列里消息的队列,具体可以参考http://ifeve.com/concurrent-collections-4/
下面我们看看如何添加一个请求(Request)到消息队列:
/**
* 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 (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// Insert request into stage if there's already a request with the same cache key in flight.
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
//判断请求等待队列(该请求已经发送还没返回)是否已存在该请求
if (mWaitingRequests.containsKey(cacheKey)) {
//已有请求发送,排队等待(在已有请求发送结束后会加等待队列里的请求发送到缓存队列,详看finish(Request<?> request)
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 {
//如果该请求尚未发送则加入缓存队列
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
通过代码可以发现,如果使用缓存,则先将请求放入缓存队列,否则直接加入网络请求队列。
最后来看看如何取消特定tag的请求,这个设计有点意思:
/**
* A simple predicate or filter interface for Requests, for use by
* 定义一个过滤请求的接口
*/
public interface RequestFilter {
public boolean apply(Request<?> request);
}
/**
* 取消所有符合接口过滤条件的请求
*/
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}
/**
* 取消所有符合特定tag的请求
*/
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) {
return request.getTag() == tag;
}
});
}
首先设计一个过滤接口,然后将该接口作为过滤条件,取消特定的请求。