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

1 0
原创粉丝点击