使用过Volley的开发者应该很清楚,对于一个request只需要将它加入对Queue中即可,使用也非常简单,也许没有注意它的细节,看了下面的这个对request添加优化处理后,我们将明白Volley框架在一些细节方面做得很好,这种做事的态度值得我们学习。
下面从代码中分析Volley的add request的处理方式,首先要了解一个变量:
//重复请求的暂存区域,存储相同cachekey的request
private final Map<String, Queue<Request<?>>> mWaitingRequests =
new HashMap<String, Queue<Request<?>>>();
然后看RequestQueue的add方法:
/**
* 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.
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
//暂存相同cacheKey的request
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<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 {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
//这里添加一个正在执行的标记
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
这里分析执行过程:
1,将request加入到mCurrentRequests 中
2,如果request为不需要缓存,那么直接放入网络请求的Queue中
3,如果request为可以缓存,那么根据request的cacheKey 进行判断是否存在于mWaitingRequests中,如果已经存在,则将request放入对应的queue中暂存起来,否则在mWaitingRequests中标记对应的cacheKey 的request;
在request的的执行过程中,最终会到ExecutorDelivery类中ResponseDeliveryRunnable的run()方法,执行mRequest.finish(“done”),从而到mRequestQueue.finish(this);这里我们看RequestQueue的finish()方法怎么处理的:
/**
* Called from {@link Request#finish(String)}, indicating that processing of the given request
* has finished.
*
*/
<T> void finish(Request<T> request) {
// Remove from the set of requests currently being processed.
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
//核心操作
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
// Process all queued up requests. They won't be considered as in flight, but
// that's not a problem as the cache has been primed by 'request'.
mCacheQueue.addAll(waitingRequests);
}
}
}
}
这里核心的操作为将mWaitingRequests中的request加入到mCacheQueue队列中。这样操作的好处就是针对多个cacheKey相同的request,等待第一个request执行完成后再执行剩下的request,这样就不用同时执行重复的网络操作,减少网络操作,提高响应速度;那么这种会在什么情况下发送呢?比如:网络较差的环境下,如果我们不停的刷新界面,导致多个相同的请求,这样势必会影响网络的处理能力,如果我们第一个请求执行完成后有了缓存数据,那么从缓存数据中取数据将要比网络中取数据快得多。当然同时执行多个cachekey相同的request的这种场景不多,但是Volley已经为我们考虑进去了,由此可以说明Volley在细节方面处理得很好。
平时我们在开发项目中,有时为了速度和效果忽略了某些细节,也许这些细节看似没多重要,但是要做好一款产品或者一个框架,细节的处理将决定我们的成败。