Volley源码解析
导语
阅读源码是已经入门的Android开发者必经之路,这是提高自己对代码理解的一步。但是一开始阅读源码不能深入的去读细节部分,而是理顺源码的主要部分,理解它的实现原理即可,否则一旦陷入会导致很难读懂,可能会失去阅读源码的兴趣。volley现在已经不是主流的网络请求框架,但是其结构并不复杂,容易理解,因此选择此框架来学习。
一、Volley的使用
RequestQueue requestQueue =
Volley.newRequestQueue(context.getApplicationContext());// 创建请求队列实例
StringRequest request = new StringRequest(url,
new Listener<String>() {
@Override
public void onResponse(String response) { // 成功返回数据,做UI更新等操作},
new ErrorListener() {
@Override
public void onErrorResponse(VolleyError arg0) {// 失败,做相应处理}
});
requestQueue.add(request);// 将请求添加至请求队列中
以上是volley常规的使用方式。先是创建请求队列,这个一般在采用单例模式创建然后用于全局;接着创建请求,请求有StringRequest、JsonObjectRequest等等,主要区别就是返回数据格式不同。最后将请求添加至请求队列(循环处理请求)。
二、Volley解析
阅读源码的方法很简单,就是跟进代码,一点点理解。我们从创建请求队列实例开始跟进。(可能会省略部分不重要的源码)
2.1 Volley
跟进Volley.newRequestQueue(context.getApplicationContext()),可以发现此类是volley框架的入口,源码如下:
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), "volley");
if(stack == null) {// 在api9以前使用httpClient网络请求,而api9以后使用httpUrlConnection,但是在api23以后已经不支持httpClient了
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack();// 用httpUrlConnection请求并获取结果
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));// 用httpClient请求并获取结果
}
}
BasicNetwork network1 = new BasicNetwork((HttpStack)stack);// 处理请求,使用httpStack获得请求结果封装后返回
RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);// 创建请求队列,并传入缓存对象以及处理请求对象
queue1.start();// 启动了缓存线程以及网络请求线程
return queue1;
}
2.2 RequestQueue
我们先跟进queue1.start(),读源码的时候不能一直按顺序读,如果先跟进BasicNetWork或DiskBasedCache会很难理解,但是我们能从语义上基本能看出他们的作用,一个是网络请求一个是磁盘缓存。queue1.start()能看出是队列启动的入口,从这里进入会遇到使用BasicNetWork或DiskBasedCache这些类的时候。
public void start() {
this.stop();
this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue,
this.mNetworkQueue, this.mCache, this.mDelivery);// 这是一个缓存线程,已缓存结果的一些请求不必再去进行网络请求,直接在mCacheQueue里拿结果,同时缓存会有过期、新鲜度等判断,如果不符合条件则取mNetworkQueue里的网络请求。mCache是缓存结果的一个实例,mDelivery是分发结果的实例。
this.mCacheDispatcher.start();
for(int i = 0; i < this.mDispatchers.length; ++i) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue,
this.mNetwork, this.mCache, this.mDelivery);// 网络请求线程,网络请求时间长,因此需要多个线程一起执行,默认是4个,可以自己设置。
this.mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
2.3 NetworkDispatcher
先讲解NetworkDispatcher是因为我想要根据一开始执行的流程走, 只有进行过网络请求的才有结果缓存,这样思路比较顺点。NetworkDispatcher是一个处理网络请求的线程。
public void run() {
while(true) {
long startTimeMs;
Request request;
while(true) {// 只要网络请求队列里出现请求,马上取出执行。
startTimeMs = SystemClock.elapsedRealtime();
try {
request = (Request)this.mQueue.take();// 从队列拿出请求
break;
} catch (InterruptedException var6) {
if(this.mQuit) {
return;
}
}
}
try {
request.addMarker("network-queue-take");
if(request.isCanceled()) {// 这些地方我们就不必去深究,只要语义上理解就好,很明显这是请求取消了,然后请求完成。传入了一些标志的字符串。
request.finish("network-discard-cancelled");
} else {
NetworkResponse e = this.mNetwork.performRequest(request);// 这里就是关键的网络请求,拿到封装好的结果(其实是将httpResponse解析成自定义的NetworkResponse)
request.addMarker("network-http-complete");
if(e.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
} else {
Response volleyError1 = request.parseNetworkResponse(e);// 再次将NetWorkResponse对象转换成自定义的Response对象。Response最终可解析成我们需要的数据,而NetworkResonse只是过渡的一个对象。
request.addMarker("network-parse-complete");
if(request.shouldCache() && volleyError1.cacheEntry != null) {
this.mCache.put(request.getCacheKey(),
volleyError1.cacheEntry);// 这里就是缓存结果
request.addMarker("network-cache-written");
}
request.markDelivered();
this.mDelivery.postResponse(request, volleyError1);// 这也是关键,将结果分发给request,request再传给监听器回调给主线程
}
}
} catch (VolleyError var7) {
var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
this.parseAndDeliverNetworkError(request, var7);
} catch (Exception var8) {
VolleyError volleyError = new VolleyError(var8);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
this.mDelivery.postError(request, volleyError);
}
}
}
2.4 BasicNetwork
该类实现了Network接口,主要的作用是进行网络请求并返回结果。 跟进以上代码的mNetwork.performRequest(request)
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while(true) {
HttpResponse httpResponse = null;
Object responseContents = null;
Map responseHeaders = Collections.emptyMap();
try {
HashMap e = new HashMap();
this.addCacheHeaders(e, request.getCacheEntry());// 取出request中的缓存实体,将部分信息缓存到缓存头中(可能无法理解为什么要有缓存头,不用死缠烂打,先看下面)
httpResponse = this.mHttpStack.performRequest(request, e);// 这里就是真正的请求数据并返回结果。
StatusLine statusCode2 = httpResponse.getStatusLine();
int networkResponse1 = statusCode2.getStatusCode();// 状态码,用于判断服务端的结果是否改变
responseHeaders = convertHeaders(httpResponse.getAllHeaders());// 解析返回结果的响应头
if(networkResponse1 != 304) {// 304表示在一定时间内结果是否有改动
byte[] responseContents1;
if(httpResponse.getEntity() != null) {
responseContents1 = this.entityToBytes(httpResponse.getEntity());// 将httpEntry实体转换成字节数组
} else {
responseContents1 = new byte[0];
}
long requestLifetime1 = SystemClock.elapsedRealtime() - requestStart;
this.logSlowRequests(requestLifetime1, request, responseContents1, statusCode2);
if(networkResponse1 >= 200 && networkResponse1 <= 299) {
return new NetworkResponse(networkResponse1, responseContents1, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);// 返回一个根据服务端返回结果来构建的NetworkResponse对象
}
throw new IOException();
}
Entry requestLifetime = request.getCacheEntry();// 执行到这里说明服务端没有改动结果,因此直接取缓存
if(requestLifetime == null) {
return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
}
requestLifetime.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(304, requestLifetime.data, requestLifetime.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException var12) {
}
}
}
2.5 HurlStack
很明显,我们要跟进核心部分mHttpStack.performRequest(request, e),这里是通过接口的向上转型,我拿HurlStack实现类来讲解,因为HttpStack已经基本用不到了。
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap map = new HashMap();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
//这部分是参数的拼接,准备用来请求
URL parsedUrl1 = new URL(url);
HttpURLConnection connection = this.openConnection(parsedUrl1, request);
Iterator responseCode = map.keySet().iterator();
while(responseCode.hasNext()) {
String protocolVersion = (String)responseCode.next();
connection.addRequestProperty(protocolVersion, (String)map.get(protocolVersion));
}
setConnectionParametersForRequest(connection, request);
ProtocolVersion protocolVersion1 = new ProtocolVersion("HTTP", 1, 1);
int responseCode1 = connection.getResponseCode();
if(responseCode1 == -1) {
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
} else {
BasicStatusLine responseStatus = new BasicStatusLine(protocolVersion1, connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));// 这句才是真正的网络请求,返回结果后赋予response
Iterator var12 = connection.getHeaderFields().entrySet().iterator();
while(var12.hasNext()) {
Entry header = (Entry)var12.next();
if(header.getKey() != null) {
BasicHeader h = new BasicHeader((String)header.getKey(), (String)((List)header.getValue()).get(0));
response.addHeader(h);
}
}
return response;
}
}
2.6 ExecutorDelivery
在通过HurlStack获取HttpResponse结果后,我们返回到BasicNetwork继续读代码,发现将HttpResponse转换成NetworkResponse并且返回到NetworkDispatcher,最后执行了mDelivery.postResponse(request, volleyError1)分发结果。
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
//调用了自定义的Runnable,其实是将子线程切换到了主线程
this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable));
}
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) {
this.mRequest = request;
this.mResponse = response;
this.mRunnable = runnable;
}
public void run() {
if(this.mRequest.isCanceled()) {// 请求取消
this.mRequest.finish("canceled-at-delivery");
} else {
if(this.mResponse.isSuccess()) {
this.mRequest.deliverResponse(this.mResponse.result);// 分发结果给request,request最终分发给监听器
} else {
this.mRequest.deliverError(this.mResponse.error);
}
}
}
}
2.7 CacheDispatcher
上面已经完成了一个完整的网络请求并且将结果分发给监听器,供开发者使用结果。在之前我们先讲解了NetworkDispatcher,并且也看到了mCache.put(request.getCacheKey(), volleyError1.cacheEntry),将缓存实体存入缓存对象中。CacheDispatcher也是一个线程,循环来取缓存队列里的请求:
public void run() {
//省略部分代码...
this.mCache.initialize();// 缓存类初始化,主要是在SD卡里创建缓存文件夹
while(true) {
final Request e = (Request)this.mCacheQueue.take();// 拿出缓存请求
e.addMarker("cache-queue-take");
if(e.isCanceled()) {// 请求取消
e.finish("cache-discard-canceled");
} else {
Cache.Entry entry = this.mCache.get(e.getCacheKey());// 根据key拿到缓存实体
if(entry == null) {
e.addMarker("cache-miss");
this.mNetworkQueue.put(e);// 如果没有缓存则加入网络请求队列进行网络请求
} else if(entry.isExpired()) {// 过期的也要加入网络请求队列
e.addMarker("cache-hit-expired");
e.setCacheEntry(entry);
this.mNetworkQueue.put(e);
} else {
e.addMarker("cache-hit");
Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));// 将NetworkResponse转换成Response
e.addMarker("cache-hit-parsed");
if(entry.refreshNeeded()) {// 新鲜度判断,如果需要刷新则在分发结果的同时加入网络请求队列,去获取最新的结果
e.addMarker("cache-hit-refresh-needed");
e.setCacheEntry(entry);
response.intermediate = true;
this.mDelivery.postResponse(e, response, new Runnable() {
public void run() {
try {
CacheDispatcher.this.mNetworkQueue.put(e);
} catch (InterruptedException var2) {
;
}
}
});
} else {
this.mDelivery.postResponse(e, response);// 分发结果(和NetworkDispatcher一样)
}
}
}
}
}
三、总结
随着一点点的跟进代码,我们理清了Volley的工作流程:
1.初始化网络请求类(BasicNetwork)、缓存类(DiskBasedCache)等,创建请求队列(RequestQueue)并启动缓存请求线程(CacheDispatcher)和网络请求线程(NetworkDispatcher )。
2.CacheDispatcher和NetworkDispatcher不断轮询取出请求,如果有缓存则进行过期时间、新鲜度等判断,来决定是从Cache中取缓存还是再去进行网络请求。网络请求通过BasicNetwork调用HttpStack来返回请求结果后会进行缓存(除非你自己设置不进行缓存)。
3.最后通过ExecutorDelivery实现类分发给我们创建的Request中,而根据我们使用的Request(StringRequest、JsonRequest、ImageRequest等)返回对应的数据结构。
以上就是Volley的主要工作原理,我觉得先看懂主要原理是重要的一步,理解原理后再去看代码细节,比如Volley中的PoolingByteArrayOutputStream(继承ByteArrayOutputStream并给二进制输出流增加缓存区)、HttpHeaderParser(用来解析返回结果的头部)等。
参考:
http://a.codekk.com/detail/Android/grumoon/Volley%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90