还是先自己分析一遍源码,在看大神们的分析更有用。
研究Volley源码,首先从RequestQueue开始,
1.RequestQueue
/** The cache triage queue. */
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
/** The queue of requests that are actually going out to the network. */
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
分别用来存储可以从缓存获取的request与需要联网获取的request的阻塞队列
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
* @param delivery A ResponseDelivery interface for posting responses and errors
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
首先看RequestQueue的构造函数,其中cache,network分别提供本地缓存查询与联网查询的接口。
在class volley中我们可以看到,我们平时默认创建的时候,cache采用的是DiskBasedCache本地缓存,而network则根据版本选择HurlStack或HttpClientStack。
NetWorkDispacher的话,实际上继承自Thread
/**
* Provides a thread for performing network dispatch from a queue of requests.
*
* Requests added to the specified queue are processed from the network via a
* specified {@link Network} interface. Responses are committed to cache, if
* eligible, using a specified {@link Cache} interface. Valid responses and
* errors are posted back to the caller via a {@link ResponseDelivery}.
*/
public class NetworkDispatcher extends Thread {
在这里相当于用一个threadPoolSize大小的数组来存储线程的引用,threadPoolSize默认为4。
最后delivery则用来处理联网或从缓存中获取的结果。
这样,请求队列初始化就完成了,然后需要调用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();
}
}
Start函数中的方法很简单,
其中CacheDispatcher也是一个Thread子类
所以整个函数就是,启动一个处理cache请求的线程,还有创建threadPoolSize个联网请求的线程,默认会创建4个。
然后CacheDispatcher和NetworkDispatcher就可以开始处理各自请求队列中的请求了。这两个线程稍后在看。现在还剩下一个比较重要的方法,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();
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;
}
}
首先先设置本次request的处理队列为当前的RequestQuque
之后将该方法添加到mCurrentRequests中,本质是一个set。
然后判断该请求是否允许从cache获取信息,如果不允许,则直接讲该请求添加到mNetworkQueue联网请求队列中并返回。
如果允许,则判断mWaitingRequests(一个hashMap,用来保存正在处理中的请求)中是已经有一个相同请求(这里相同时是通过getCacheKey来判断的,也就是判断request中的url是否相同)正在处理了,如果查到有相同的请求正在处理,则获取该请求的一个等候队列,将本次请求加入等候队列,等候结果的返回。
如果没有相同的请求在处理,则将本次请求加入mWaitingRequest中,表示这个请求正在被处理,以后相同请求到来,直接等候该结果返回即可,然后将本次请求加入mCacheQueue队列中,先尝试从缓存中获取信息。
还剩下几个方法:
cancleAll方法很简单,遍历存储当前请求的集合mCurrentRequest,找到tag相同的请求后,设置为cancel,这样在处理流程中,检测到request已被取消则不会再处理。
Finish方法,在request请求完成后,会调用request中的finish,也相应调用了requestQueue中的finish方法。将本次request从mCurrentRequest中移除。然后不要忘记之前有个相同请求的等待队列存在mWaitingRequests中,如果本次request允许缓存,则可以把结果给这个队列里的request使用,否则不行。然后从mWaitingRequests这个Map中取出相同请求的等待队列,然后将所有请求加入mCacheQueue中,从缓存去获取值。
接下来先看看CacheDispatcher怎么来处理mCacheQueue中请求的。
2.CacheDispatcher
直接关注它的run方法
@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
// at least one is available.
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;
}
continue;
}
}
}
首先设置本线程优先级为THREAD_PRIORITY_BACKGROUND
,一个比较低的优先级,不太影响用户线程。
mCache.initialize();
这个方法是初始化缓存,在DiskBaseCache中有具体实现。
然后就进入了一个死循环中,真正的工作开始了。
mCacheQueue.take()
首先使用take方法,从队列中取出一个Request,队列是BlockingQueue阻塞队列,使用take方法,当队列为空时,则会阻塞。非空时则会获取队列头的一个元素并移出队列。
在处理时,每一步都会addMarker,应该是用来标记本request处理到了哪一步。
然后首先先判断
if (request.isCanceled()) {
先判断本次request是否被取消了,volley的一大好处就是可以通过cancelAll()与tag随时取消一个不需要进行的请求。如果取消了,则不用再做处理,直接调用Request的finish方法返回。
如果没被取消,则继续,
// 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;
}
从mCache中查找是否有本次请求的缓存,请求用的key就是request中的url,如果缓存中没有找到,则将本request加入到mNetQueue需联网请求的队列中。如果找到了则继续。
// 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;
}
找到了的话,也不是直接用,而是判断缓存是否过期,如果过期了,还是会联网请求,不过也会把缓存的结果加入到request中,可能如果联网请求失败就用这个了。
如果上方几步都通过了,则可以直接从缓存获取本次request的结果了。
然后会
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
先将缓存获取的数据封装为NetworkResponse,然后调用request的parseNetworkResponse生成一个Response。
最后一步还要判断一下,当前缓存是否要求更新,这里的话和上方不太一样。先说结果,如果不需要更新,则将本次Response交给mDelivery。否则还会从联网请求。
mDelivery先放一下,先研究NetWorkDispatcher
3.NetWorkDispatcher
NetWorkDispatcher和CacheDispatcher前边类似,
也是先从mNetworkQueue阻塞队列中take一个元素。然后判断是否被取消了,没取消则继续处理。
addTrafficStatsTag(request);
这个方法在API版本14以上才会启用,需要给request添加一个tag,然后这个tag就是url中的host字符串的hashcode
URL's host component
不很懂,跳过。
然后
// Perform the network request.
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
在这里就这直接调用网络API的方法,把本次请求发了出去了,并创建NetworkResponse对象接收请求结果。
这里的mNetWork在api9以上,使用的都是HurlStack,api9以下则使用HttpClientStack,这个在其他文章中也说了。
接下来处理返回结果,首先检查状态码如果是304 NotModify (表示自上次请求后,内容未修改),这样的话,不会返回数据的,然后直接返回。
// Parse the response here on the worker thread.
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
然后则会根据网络返回的结果,与cacheDispatcher中类似,也会生成一个Response返回。
然后如果本次请求允许缓存并且缓存值非空,则将本次缓存请求。
这里缓存值是否为空,跟具体的request中parseNetworkResponse的实现有关,parseNetworkResponse是个抽象方法。
最后
mDelivery.postResponse(request, response);
和CacheDispatcher中一样也是调用mDelivery的postRespones放法返回结果。
4.ResponseDelivery
接下来可以看mDelivery源码了。
ResponseDelivery也是一个接口,他的默认实现方法则是下边这个,
new ExecutorDelivery(new Handler(Looper.getMainLooper())
在这里
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
具体实现是ExecutorDelivery
/**
* Creates a new response delivery interface.
* @param handler {@link Handler} to post responses on
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
可以看到,ExecutorDelivery将主线程关联的Handler作为参数传了进来,这样实现了个Executor可以将command传入主线程中执行。
然后我们在CacheDispatcher和NetworkDispatcher中调用的都是delivery中的postResponse方法,我们看下在这里的具体实现。
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
在做完标记后,调用了
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
方法,在上边构造函数中我们已经看到,会将new ResponseDeliveryRunnable(request, response, runnable)
传入到主线程中运行。
然后我们继续分析ResponseDeliveryRunnable,一个内部类
构造方法中传入的参数,request是request本身,response是联网请求或缓存请求获取来的结果,至于runnable,CacheDispatcher和NetworkDispatcher中传来的都是null。
然后看run方法
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}
先判断请求是否这时被取消了,
没有则根据本次返回结果成功与否,分别调用deliverResponse或deliverError
然后就结束了。
然后deliverResponse实际上调用的就是mListener.onResponse(response),也就是我们需要写的返回成功的处理方法,
deliverError则调用的mErrorListener.onErrorResponse(error);
至此,Volley的基本流程分析完了。
回顾一下:
说说流程,
1.当往requestQueue中添加一个新的request时,一般情况下,如果此时有相同的url的request请求正在被处理,则将该request加入一个等待队列中等待正处理的请求返回,否则的话,则处理这个请求。
2.首先会将其加入mCacheQueue缓存请求队列中,由CacheDispatcher从本地缓存中找是否有对应结果。查找时根据request中的url进行。如果找到了则用缓生成Response并调用delivery.postResponse 返回结果。如果没有或者缓存过期,则将该请求加入mNetworkQueue联网网球队列中。
3.由NetworkDispatcher处理,将该请求联网发出,然后返回后,也同样交给delivery.postResponse 返回结果。
4.delivery.postResponse默认情况下,会组装一个ResponseDeliveryRunnable线程由handler传递给主线程运行,在这里调用request的deliverResponse
方法,也就是调用我们生成request时所写的ResponseListener和ErrorListener。
如果觉得写得不好,可以去看看大神们的解析
http://blog.csdn.net/yanbober/article/details/45307217
http://blog.csdn.net/guolin_blog/article/details/17656437