Volley源码简单解析
来源:互联网 发布:mmd工口动作数据 编辑:程序博客网 时间:2024/05/18 03:20
还是先自己分析一遍源码,在看大神们的分析更有用。
研究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方法
@Overridepublic 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方法,我们看下在这里的具体实现。
@Overridepublic void postResponse(Request<?> request, Response<?> response) { postResponse(request, response, null);}@Overridepublic 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
- Volley源码简单解析
- 【Volley】Volley源码解析
- volley源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- Volley 源码解析
- HDOJ 1010 Tempter of the Bone(剪枝+DFS)
- 解决ScrollView中嵌套Listview,Listview中嵌套Listview显示不完整和滑动冲突的问题
- 【C++11】30分钟了解C++11新特性
- 分享一个移动端和PC间的判段方法
- Mysql异常:apply security settings 1045
- Volley源码简单解析
- phonegap笔记-滑轮效果,竖形走马灯效果
- Java源码阅读-StringBuffer
- UDP丢包的问题
- Binary Tree Maximum Path Sum
- Xml利用Pull解析
- jsp页面引入css文件报错
- Scanner类输入
- DAY03_python2 与 python3的区别