更多关于安卓源码分析文章,请看:安卓源码分析
Volley源码分析系列:
1.聊下Volley源码(整体流程)
2.聊聊Volley源码(网络请求过程)
3.聊聊Volley源码(缓存流程)
上一篇博文基本溜了一圈Volley的整体流程 聊下Volley源码(整体流程),但是各个细节都没讲到,这篇文章准备讲下网络请求的细节过程。
像上一篇博文所说,每个Volley请求,基本都是创建一个请求队列RequestQueue对象和一个具体的请求(比如StringRequest),然后将该请求add到RequestQueue对象中即可。而在创建请求队列的过程中,又启动了CacheDispatcher(缓存任务分发器)和若干个NetworkDispatcher(网络请求分发器),其实它们都是Thread的子类,而在它们的run方法中,是不断循环取出一个队列(不是RequestQueue哦)的Request去执行相应的任务。今天主要谈下执行网络请求的详细流程。
一切,还得从RequestQueue的add方法说起。
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并不漫长,直接都复制在这里。首先说明下,RequestQueue 中维护了两个基于优先级的 Request 队列,缓存请求队列和网络请求队列,分别是 PriorityBlockingQueue mCacheQueue和 PriorityBlockingQueue mNetworkQueue,维护了一个正在进行中,尚未完成的请求集合Set mCurrentRequests,
维护了一个等待请求的集合Map mWaitingRequests,如果一个请求正在被处理并且可以被缓存,后续的相同 url 的请求,将进入此等待队列,每个key对应一个相同请求的集合。
在add中,首先将请求与当前的RequestQueue关联起来,然后将请求添加到mCurrentRequests,这意味着请求已经正在进行中。然后给请求设置序列号,因为我们请求是基于优先级加入请求队列的(即优先级高的排在队列前面,更早被取出)。接着判断下请求是否需要缓存,默认的请求都是需要缓存的。如果不需要缓存,则加入到请求队列mNetworkQueue中,并返回add方法。如果需要缓存的,则先提取请求对应的cache key,这里为请求url,然后判断下这个cache key在mWaitingRequests中有没有对应的value,即与请求相同url的请求的队列,有的话则将请求加入到该队列,如果没有的话则将cache key作为key,nul作为value添加入mWaitingRequests中。这里的mWaitingRequests的主要作用,就是避免重复的网络请求,它首先记录第一个新的url请求的url作为key,然后如果在该请求在进行中的时候后续有相同url的请求add到RequestQueue中,则将这些请求添加到对应cache key的队列中就行了。然后,在一个请求不与正在请求的请求的url重复的时候,将该请求添加到mCacheQueue中,add到此结束。
这时你可能会问到,那些默认要缓存的请求,不用添加到网络请求队列么?(反正我当时疑惑了好久= =)简单来讲,就是CacheDispatcher在取出缓存队列的请求的时候,会判断该请求的响应数据是否已经被缓存到本地磁盘,如果没有缓存响应的数据,就会被添加到请求队列中(有缓存的话也要进行新鲜度检测确定缓存是否可用),这部分的细节下一篇博文再讲。
接下来来到NetworkDispatcher的run方法:
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// Take a request from the queue.
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) {
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);
}
}
}
首先会将线程的优先级设置为比普通线程低一点,为的是尽量确保线程不影响UI的体验。然后进入一个死循环,从请求队列中取出请求,然后会判断下请求是否已经被取消,如果取消则对请求调用finish并重新循环取下一个请求(即该请求已经不再处理)。finish的处理就是将请求从RequestQueue的mCurrentRequests和将该请求移除。(这里取消请求,往往运用在和Activity生命周期联动上,在Activity的destroy方法取消掉该Activity的所有请求。主要是防止内存泄漏)
如果是未被取消请求,则调用:
NetworkResponse networkResponse = mNetwork.performRequest(request);
这里的network默认为BasicNetwork,是主要的执行请求的类,performRequest方法看名字都可以猜出就是在执行请求的方法,它又调用了一个实现HttpStack的类对象去真正执行Http请求。
拿到NetworkResponse之后,网络请求的数据就到手了,封装在这个NetworkResponse对象中。这时候数据还是二进制的,然后对响应的数据进行解析:
Response<?> response = request.parseNetworkResponse(networkResponse);
这是在具体的Request类中要重写的方法,是将二级制数转化为对应的所需要的数据形式。接着如果请求是需要缓存的,就将请求相关数据缓存在磁盘中,mCache默认是DiskBasedCache。
最后执行:
mDelivery.postResponse(request, response);
这也是上一篇文章说到过的,将响应结果传递到主线程传给客户端代码。
这是run的基本流程,当然在要停止该线程的时候可以退出死循环的,注意这段:
catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
这里是通过中断线程以及设置停止标志位完成线程的停止的。这里调用了RequestQueue的stop方法就会停止所有请求和缓存线程的循环。
再看下请求类BasicNetwork的performRequest方法(省去后面关于请求异常相关处理代码):
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
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) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// 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,
SystemClock.elapsedRealtime() - requestStart);
...此处略去若干捕获异常相关代码...
首先会调用addCacheHeaders方法,目的是添加该请求缓存相关的请求头(如果有相同url请求请求过的话,这部分下一篇专门讲缓存再讲,会涉及到Http缓存机制的内容),如果有的话。然后通过真正的请求类HttpStack具体类对像得到HttpResponse对象,这时候就拿到响应数据了。然后获取状态码和响应头,接下来会对状态码做判断确定是否取出缓存数据(这一部分也是涉及Http缓存机制,在下一篇博文介绍),如果不是取缓存则调用:
responseContents = entityToBytes(httpResponse.getEntity());
将响应体转化为二进制数据,接着封装响应体的二进制数据和状态码以及响应头为一个NetworkResponse对象,返回给NetworkDispatcher对象准备传递到客户端主线程。
再来看看这里执行请求的HttpStack具体对像,就像上一篇文章说的,如果是安卓9以上版本,HttpStack为HurlStack,是以安卓的HttpUrlConnection作为请求类的。以HurlStack为例,在执行performRequest的时候,会由url对象获取一个HttpUrlConnection对象,根据Request对象的属性,添加相应的请求方法、请求头、请求参数、请求实体,然后得到HttpUrlConnection的InputStream,在由它得到相应的响应实体、响应头等数据,然后将这些数据封装为一个BasicHttpResponse 返回给BasicNetwork对象。
这时候已经拿到数据了,那么就看下NetworkDispatcher是如何将线程传递到客户端主线程上:
mDelivery.postResponse(request, response);
这里的mDelivery默认是ExecutorDelivery,其实,在我们使用Volley的newRequestQueue方法创建一个RequestQueue的时候,就会调用到RequestQueue的这个构造方法:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
这里创建了一个ExecutorDelivery对象,并且传入了Looper为主线程Looper的Handler对象(这里如果大家不熟悉Handler的Looper,可以看下我这篇博文 全面分析Handler消息机制)
看下ExecutorDelivery的构造方法:
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);
}
};
}
创建了一个Executor的实现类对象,execute方法为调用刚才的Handler的post方法。
传递结果到客户端最终会执行到以下方法:
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
ResponseDeliveryRunnable是一个Runnable,最终会被Handler的post执行到。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();
}
}
注意到第十行:
mRequest.deliverResponse(mResponse.result);
实质上是调用了请求的deliverResponse,这是请求要重写的方法,比如StringRequest的deliverResponse方法:
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
是不是豁然开朗呢。是的,mListener就是客户端传入的Response.Listener(请求错误的话是Response.ErrorListener)。
说了这么多,可能比较乱,希望各位看了有所收获吧~~下一篇聊聊Volley源码(缓存流程)