大概看了一下volley的源码,源码写得很整洁,代码结构清晰,可扩展性也是很好。
关于volley的源码解析,网上已经有很多的资料,这里就不再进行源码的解析了。但是关于volley的网络请求架构还是值得我们学习的。这里就简单分析一下volley的网络请求架构。
这是volley的网络请求架构图:
整体流程:
首先,网络请求会被加入到请求队列中。先判断网络请求request是否可以缓存的(默认都是可以缓存的),如果不可以缓存,则直接添加到mNetworkQueue网络请求队列中;如果可以缓存,则添加到mCacheQueue缓存队列中。
然后,Volley会维护一个缓存调度线程(cache线程)和一个网络调度线程池(net线程),它们对应两个分发器CacheDispatcher和NetworkDispatcher,它们分别处理mCacheQueue和mNetworkQueue队列中所有请求。
CacheDispatcher中的处理流程是:先从mCacheQueue中取出一个request;然后从本地缓存中存取request对应的数据,如果无法取到对应的数据,则把request添加到mNetworkQueue中,如果取到数据,则把数据进行解析,然后把结果分发到UI线程中进行处理。
NetworkDispatcher的处理流程:先从mNetworkQueue中取出一个request;然后调用BasicNetwork的performRequest()方法进行网络数据的请求,得到网络数据Response后,对Response进行解析;判断,如果request是需要缓存的,就把得到的数据进行缓存,否则直接把数据分发到UI线程进行处理。
这样就完成一个网络请求数据的整个过程。
源码解析
这里只会对volley中的核心的类及类中的核心方法进行解析。
类:RequestQue.java
方法如下:
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;
}
}
解析:
这是一个添加网络请求的方法,我们在客户端使用的时候,只需要简单调用这个add()方法就可以了。
这个方法中涉及到好几个全局变量,我们先分析该变量的作用:
mCurrentRequests:保存所有的添加过得request请求。当我们需要进行网络请求时,会把request添加到该集合中;当请求完成后,会把request从集合中移除。当我们需要取消某个request请求时,会从该集合中找到对应的request,然后执行request.cancel()操作。
mNetworkQueue:保存需要NetworkDispatcher处理的所有request。NetworkDispatcher会轮询从该集合中取得request进行网络数据请求操作。
mWaitingRequests:保存已经添加到请求列表中但是还没处理完成的request。当我们add()一个新的请求的时候,会从该集合中进行判断该集合中是否已经存在该request了,如果是则不添加到mCacheQueue请求列表中,否则添加到请求列表中。当请求完成后,request会从该结合中移除。
mCacheQueue:保存需要CacheDispatcher处理的所有的request。
CacheDispatcher会轮询从该集合中取得request进行处理。
该方法对添加网络请求的流程进行了一定的优化操作。
流程如下:
把请求添加到mCurrentRequests集合中进行保存;再判断request请求是否可以缓存,如果不可以缓存则直接添加到mNetworkQueue中,否则先判断request是否已经添加到请求列表中(是否包含在mWaitingRequests中),如果没有,则添加到请求列表mCacheQueue中。该方法的逻辑处理可以防止请求多次添加到请求列表中。
类:CacheDispatcher.java
方法如下:
@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;
}
}
}
解析:
CacheDispatcher是一个继承Thread的线程类,该run方法就是线程启动后执行的方法。
线程第一次执行的时候,都会初始化缓存,缓存中有保存需要缓存的数据。然后就会进入死循环,不断轮询从mCacheQueue列表中取得request进行网络数据的请求操作。
轮询流程:
1、先从mCacheQueue队列中取得一个request请求,
2、判断若请求已经取消,则直接finish请求,进行下一次轮询;否则从缓存中取request对应的数据,继续下面的步骤。
3、判断从缓存中获得的数据若是空的,则把请求添加到mNetworkQueue队列中,进行下一次轮询。若不是空,则进行下面的步骤。
4、判断取得的数据是否已经过期了,若已经过期,则把请求添加到mNetworkQueue中重新请求数据。若没有过期则继续下面的步骤。
5、把得到的数据解析解析。再判断数据是否需要刷新,若不需要刷新,则直接把数据分发到UI线程进行处理。若数据需要刷新,则先把数据分发到UI线程,再把请求添加到mNetworkQueue,获取最新的数据。
这就是CacheDispatcher处理一次请求的过程,其实就是volley的缓存机制的处理。真正的网络请求操作是在NetworkDispatcher中处理。
类:NetworkDispatcher.java
方法如下:
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
try {
// Take a request from the queue.
// 获取request对象
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// If the request was cancelled already, do not perform the
// network request.
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// Perform the network request.
// 访问网络,得到数据
NetworkResponse networkResponse = mNetwork
.performRequest(request);
request.addMarker("network-http-complete");
// If the server returned 304 AND we delivered a response
// already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified
&& request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
Response<?> response = request
.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 写入缓存
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for
// 304s.
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back.
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
mDelivery.postError(request, new VolleyError(e));
}
}
}
NetworkDispatcher是一个继承Thread的线程类,该run方法就是线程启动后执行的方法。
线程启动后,就是不断的轮询mNetworkQueue队列中的请求,进行网络数据的请求操作。
轮询的流程:
1、从mQueue请求队列中获取一个请求。
2、判断如果请求已经取消了,则finish这次请求。否则继续下面的步骤。
3、执行访问网络操作,获取网络返回的数据。
4、判断如果获取的网络数据没有被修改,或者请求结果已经分发过,则结束这次请求,进行下一次的轮询。否则进行下面的步骤。
5、解析从网络取得的数据,如果请求的数据需要缓存,则写到缓存中。
6、分发解析后的数据到UI线程中。
这就完成了一次NetworkDispatcher处理请求的过程。
值得一提的是,这样的NetworkDispatcher处理线程一共有四个,也就是说能同时进行网络数据的请求的个数是四个,只能等四个中的某一个请求处理完成后,才能处理下一个请求。