前言
很早之前就写过关于Volley的文章,但是那时候对源码的厉害不不够深刻,所有没有在文章中作出源码的分析,最近又重新继续撸羊毛一样看了一篇,许多问题顿时清楚了许多,虽然你可能会说现在流行的Android网络框架有多种,okhhtp,Retrofit等等,但是作为Google官方推荐的网络框架,还是值的我们去深思学习的。
这篇文章我主要是解析源码对于具体的使用我就不做过多的说明了,如果你也想阅读源码可以从官网下载或者直接在你的编辑器中查看
Volley解剖
1、流程图
上面是Google关于Volley的官网上给出的图片,如果看到这种图你觉得一头雾水的话,那你可以先大概了记忆一下,接下来我们围绕这张图一步步看个究竟。
2、源码解析
Volley.java
我们使用Volley的第一步都是先获取一个RequestQueue对象,那么首当其冲我们先从它入手。我们进入到Volley类
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (HttpStack)null);
}
只是调用了newRequestQueue的方法重载并且第二个参数传入null,我们在看看这个方法(其实Volley.class有几个具体的newRequestQueue方法,不过基本都是互相调用的)
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
return newRequestQueue(context, stack, -1);
}
同样的调用newRequestQueue的方法重载,再进去看看
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), "volley");
String userAgent = "volley/0";
try {
String network = context.getPackageName();
PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
userAgent = network + "/" + queue.versionCode;
} catch (NameNotFoundException var7) {
;
}
//判断stack是否为空 并根据版本号创建不同的Http对象
if(stack == null) {
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//通过stack够着一个处理网络请求的对象
BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
RequestQueue queue1;
//构建RequestQueue 并启动和还回它
if(maxDiskCacheBytes <= -1) {
queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
} else {
queue1 = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network1);
}
queue1.start();
return queue1;
}
找到具体实现的方法, 可以发现,其中通过if语句判断stack是否等于null,如果为null则创建一个对象,这个对象在API Level>=9的时候创建基于HttpURLConnection 的 HurlStack,如果API Level<9,则采用基于 HttpClient 的 HttpClientStack创建
创建好HttpStack后,一用这个satck构造了一个BasicNetwork对象用来处理网络请求,并且构造一个代表缓存(Cache)的基于Disk的DiskBasedCache对象,最后将网络(network1)对象和缓存(Cache)对象传入构建一个 RequestQueue,启动这个 RequestQueue,并返回它。
关于HttpURLConnection 和 AndroidHttpClient(HttpClient 的封装)如何选择及原因可以查看这篇文章
RequestQueue.java
大致理清怎么创建RequestQueue后,我们再开看看RequestQueue的start()方法是怎么一回事
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
public void start() {
this.stop();//停止现在正在运行的cache和network调度线程
//创建一个CacheDispatcher实例
this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
this.mCacheDispatcher.start();
//创建NetworkDispatcher实例 length默认为4
for(int i = 0; i < this.mDispatchers.length; ++i) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
this.mDispatchers[i] = networkDispatcher;
//启动网络调度线程
networkDispatcher.start();
}
}
首先先停止现在正在运行的调度线程,然后创建一个
CacheDispatcher实例,接着在for循环里创建NetworkDispatcher并且调用他们的start()方法,默认情况for循环会执行四次。也就是说当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行。
得到了RequestQueue后,我们需要构将我们想要的Request(或者我们自定义的Request),然后调用RequestQueue的add()方法将Request传入就可以完成网络请求操作了,那么必须的我们要去add()方法的内部看看
public Request add(Request request) {
request.setRequestQueue(this);
Set var2 = this.mCurrentRequests;
//添加到当前队列mCurrentRequests中
synchronized(this.mCurrentRequests) {
this.mCurrentRequests.add(request);
}
//为请求添加序列号
request.setSequence(this.getSequenceNumber());
request.addMarker("add-to-queue");
//判断是否允许被缓存 默认允许
if(!request.shouldCache()) {
//不允许缓存则直接添加到网络队列中
this.mNetworkQueue.add(request);
return request;
} else {
Map var8 = this.mWaitingRequests;
synchronized(this.mWaitingRequests) {
String cacheKey = request.getCacheKey();
//判断是否有相同请求在等待队列中
if(this.mWaitingRequests.containsKey(cacheKey)) {
Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
if(stagedRequests == null) {
stagedRequests = new LinkedList();
}
((Queue)stagedRequests).add(request);
//加入到相同请求的等待队列中
this.mWaitingRequests.put(cacheKey, stagedRequests);
if(VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
}
} else {
//添加到等待队列中,并且加入到缓存队列中
this.mWaitingRequests.put(cacheKey, (Object)null);
this.mCacheQueue.add(request);
}
return request;
}
}
}
具体的步骤我大致在代码中给出了解释,文字容易看的眼花撩日,我特意写出了流程图:
接下来再来看看请求完成后的finish方法
void finish(Request request) {
Set var2 = this.mCurrentRequests;
//从正在请求集合中移除request
synchronized(this.mCurrentRequests) {
this.mCurrentRequests.remove(request);
}
List var10 = this.mFinishedListeners;
synchronized(this.mFinishedListeners) {
Iterator cacheKey = this.mFinishedListeners.iterator();
while(true) {
if(!cacheKey.hasNext()) {
break;
}
RequestQueue.RequestFinishedListener waitingRequests = (RequestQueue.RequestFinishedListener)cacheKey.next();
waitingRequests.onRequestFinished(request);
}
}
//request是否允许缓存
if(request.shouldCache()) {
Map var11 = this.mWaitingRequests;
synchronized(this.mWaitingRequests) {
String cacheKey1 = request.getCacheKey();
//将request从等待队列移除
Queue waitingRequests1 = (Queue)this.mWaitingRequests.remove(cacheKey1);
if(waitingRequests1 != null) {
if(VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", new Object[]{Integer.valueOf(waitingRequests1.size()), cacheKey1});
}
//将等待队列所有的请求添加到缓存请求队列中
this.mCacheQueue.addAll(waitingRequests1);
}
}
}
}
注释在代码中给出,这里就不作过多的解释了。
了解完一次用法的具体实现后,我们知道除了RequestQueue,Request,ResponseDelivery似乎最重要的就是线程调度了。那我们奋起直追再来看看缓存线程调度和网络线程调度
CacheDispatcher.java
先来看看缓存线程调度,既然说是线程调度,那我们知道CacheDispatcher肯定是继承线程的,结果发果然CacheDispatcher是集成与Thread。我们看看它主要的方法run()方法
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 初始化缓存
mCache.initialize();
//无限循环
while (true) {
try {
// 从缓存队列mCacheQueue中获取一个Request
// at least one is available.
final Request> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 请求是否取消
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 从缓存当中取出响应结果
Cache.Entry entry = mCache.get(request.getCacheKey());
//响应结果是否为空
if (entry == null) {
request.addMarker("cache-miss");
// 为空则加入到网络队列mNetworkQueue
mNetworkQueue.put(request);
continue;
}
// 缓存结果是否过期
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
// 过期则加入到网络队列mNetworkQueue
mNetworkQueue.put(request);
continue;
}
//解析缓存结果为Response
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;
}
}
}
}
代码很多,还是老方法,上我做的流程图一目了然。
NetworkDispatcher.java
接下来再看看NetworkDispatcher的主要方法run()方法
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request> request;
try {
// 从网络队列获取一个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 (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// 获取NetworkResponse
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// If the server returned 304 AND we delivered a response already,
// performRequest方法通过304响应 并且请求有传输响应
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 解析数据成Respons
Response> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 请求结果可以缓存 并且不会缓存实体不为空
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) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
和CacheDispatcher有许类似,依旧上图
其中要注意的是mNetwork.performRequest(request)方法只有在得到304响应才算成功。这里的Network其实具体实现就是我们在newRequestQueue中k看到的BasicNetwork,我们来看下它的performRequest()方法
public class BasicNetwork implements Network {
……省略
@Override
public NetworkResponse performRequest(Request> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map responseHeaders = new HashMap();
try {
// Gather headers.
Map headers = new HashMap();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry() == null ? null : request.getCacheEntry().data,
responseHeaders, true);
}
// Some responses such as 204s do not have content. We must check.
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents = new byte[0];
}
// if the request is slow, log it.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
} catch (Exception e) {
……省略
}
}
}
}
打多是一些网络请求码的处理,不做详细讲解。
最后注意在解析完了NetworkResponse中的数据之后,又会调用ExecutorDelivery的postResponse()方法来回调解析出的数据,代码如下所示:
public void postResponse(Request> request, Response> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
其中,在mResponsePoster的execute()方法中传入了一个ResponseDeliveryRunnable对象,就可以保证该对象中的run()方法就是在主线程当中运行的了,我们看下run()方法中的代码是什么样的:
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
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();
}
}
}
代码虽然不多,但我们并不需要行行阅读,抓住重点看即可。其中在第22行调用了Request的deliverResponse()方法,而这个方法就是我们在自定义Request时需要重写的另外一个方法,每一条网络请求的响应都是回调到这个方法中,最后我们再在这个方法中将响应的数据回调到Response.Listener的onResponse()方法中就可以了。
长叹一口气,到这里我们总算把Volley的执行流程基本梳理了一遍,是不是已经感觉已经思路很清晰了呢?对了,还记得在文章一开始的那张流程图吗,刚才还不能理解,现在回头看看是不是明白了呢。下面贴出一张网络上的中文图,相信你已经明白了
如果有什么错误,还请不舍赐教。