Volley源码简单解析

还是先自己分析一遍源码,在看大神们的分析更有用。

研究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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值