以StringRequest为例子,分析Volley是怎样执行一个网络请求的。
先看实现Request抽象类的StringRequest
public class StringRequest extends Request<String> {
private Listener<String> mListener;
/**
* Creates a new request with the given method.
*
* @param method the request {@link Method} to use
* @param url URL to fetch the string at
* @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors
*/
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
}
Request有3个参数:
- method 对应请求的方式
- url 请求的地址
- errorListener 发生错误时回调
StringRequest多了一个自己的listener作为成功请求的回调接口。
StringRequest实现了parseNetworkResponse方法。这个方法会传入一个NetworkResponse对象,包装了请求的响应结果。
然后根据响应结结果header里的编码格式构造一个String对象,最后使用封装了最终请求的Response类构造一个代表成功的response返回。
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
//根据编码格式构造字符串
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
//如果格式不支持编码,就构造一个默认的UTF-8编码的字符串
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
NetworkResponse封装了具体的请求内容:
public class NetworkResponse implements Serializable{
/**
* @param statusCode the HTTP status code
* @param data Response body
* @param headers Headers returned with this response, or null for none
* @param notModified True if the server returned a 304 and the data was already in cache
* @param networkTimeMs Round-trip network time to receive network response
*/
public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
boolean notModified, long networkTimeMs) {
this.statusCode = statusCode;
this.data = data;
this.headers = headers;
this.notModified = notModified;
this.networkTimeMs = networkTimeMs;
}
}
- StringReqeust 负责封装请求
- NetworkResponse 负责封装从服务器返回的请求
- Response 负责构造最终的结果。
RequestQueue的创建
Volley.newRequestQueue
有几个重载方法,最终都会执行这个:
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue;
if (maxDiskCacheBytes <= -1){
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}
else{
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
queue.start();
return queue;
}
其中执行了一些对象的创建工作:
- 创建缓存文件,文件名默认为
volley
- 创建一个
UserAgent
字符串,代表HTTP头里的客户端身份。默认为包名+APPb版本号。 - 创建执行网络请求的工具。
HurlStack
(Android 2.3及以上)或HttpClientStack
(Android 2.3以下)。Stack负责真正的HTTP请求。HurlStack
使用的是HttpURLConnection
;HttpClientStack
使用的是HttpClient
。 - 创建一个
Netwoker
对象。Netwoker
通过调用Stack
进行网络访问,并将执行结果封装为NetworkResponse
对象。 - 创建一个
RequestQueue
对象,同时创建一个DiskBasedCache
缓存对象,作为本地缓存。 - RequestQueue创建完毕之后,就调用
queue.start()
开始不断执行添加到RequestQueue中的请求。
RequestQueue的创建:
RequestQueue有3个构造函数:
//最终调用
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
//会调用最终的构造函数
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public RequestQueue(Cache cache, Network network) {
//会调用第二个
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
三个构造函数最终都是调用最上面那个。第二个构造函数,则是创建了一个ExecutorDelivery
对象,并在创建时传入了拥有UI线程的handler
。
可见ExecutorDelivery
是与主线程打交道的工具。
最后一个构造函数,则创建了:
- 一个
NetworkDispatcher
数组,数组大小为threadPoolSize
,默认为4。
NetworkDispatcher
NetworkDispatcher继承Thread。RequestQueue在创建时,创建了一个NetworkDispatcher数组,实际就是创建了一个线程数组。
public class NetworkDispatcher extends Thread {
/** The queue of requests to service. */
private final BlockingQueue<Request<?>> mQueue;
/** The network interface for processing requests. */
private final Network mNetwork;
/** The cache to write to. */
private final Cache mCache;
/** For posting responses and errors. */
private final ResponseDelivery mDelivery;
/** Used for telling us to die. */
private volatile boolean mQuit = false;
}
NetworkDispatcher拥有:
- BlockingQueue<Request<?>> 一个保存者Request的阻塞队列
- Network 执行网络访问,并返回结果
- Cache 本地缓存
- ResponseDelivery 负责与UI线程打交道。ReqeustQueue在创建时,创建的ExecutorDelivery就是一个实现了ResponseDelivery接口的类。
- volatile boolen mQuit 一个多线程可以安全访问的布尔,负责结束线程
NetworkDispatcher既然继承自Thread,那么就实现了run方法:
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
// release previous request object to avoid leaking request object when mQueue is drained.
request = null;
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);
}
}
}
- 设置当前线程的优先级为后台线程
- 从阻塞队列中获取一个request请求。这里使用的
take
方法,这个方法会阻塞线程,直到线程从队列中拿到了东西。 - 给request添加
network-queue-take
标记 - 调用netwoke的
performRequest
方法,并传入requset
获取请求的结果networkResponse - 给request添加
network-http-complete
标记 - 通过response判断是否是
304
状态码,如果是就调用request.finish()
,并跳过下面步骤。否则继续下面的步骤。 - 使用
request.parseNetworkResponse(networkResponse);
创建一个Response
对象response - 给request添加
network-parse-complete
标记 - 将请求
requset
和结果response
写入缓存。 - 调用
request.markDelivered();
表明,当前请求已被解决 - 调用
mDelivery.postResponse(request, response);
将请求和结果传递到UI线程。
这就是一个
NetworkDispatcher
线程执行一个Request
的大致流程。
ResponseDelivery->ExecutorDelivery
在NetworkDispatcher
线程中,最终结果是通过mDeliery
这个ResponseDelivery
对象传递到UI线程的。在创建NetworkDispatcher
时,mDeliery
被赋予的实际是ExecutorDelivery
的实例。ExecutorDelivery
是ResponseDelivery
接口的实现类。
ExecutorDelivery类的postResponse
方法:
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));
}
postResponse 方法调用了 mResponsePoster
的execute()
方法,并传入了一个Runnable
对象。
mResponsePoster 对象是一个 Executor
对象,并在 ExecutorDelivery 并创建时就创建。它的execute
方法,就是调用 RequestQueue在创建 ExecutorDelivery 传入的拥有UI线程的Looper的handler
的post(Runnable)
方法。
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);
}
};
}
handler post 的runnable对象是一个内部类:在 run 方法里调用了Request
对象的deliverResponse
deliverError
finish
方法。
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();
}
}
}
经过这样的转化,相当于Request
的几个方法就是在UI线程执行了:
//Requset自己实现的deliverError
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
//StringRequest实现的Request的抽象方法deliverResponse
protected void deliverResponse(String response) {
if (mListener != null) {
mListener.onResponse(response);
}
}
Requset的deliver
方法实际就是调用的在创建Request的时候,传入的Listener接口的方法。
Volley每创建一个消息队列,就创建了4个这样的NetworkDispatcher线程一直从请求队列中获取请求,然后执行,最后post到UI线程。4个线程都去拿请求,不会发生冲突是因为请求放在了BlockingQueue
中,保证了每次take
获取操作只有一个线程能获取。而且Volley的BlockingQueue使用的是PriorityBlockingQueue
,这个队列在拥有BlockingQueue功能的同时,还对队列中的请求进行了排序。
add请求操作
RequestQueue中的线程们一直在跑着,它们不断的有序的从消息阻塞队列中拿请求,执行请求,传递到UI线程。
ReqeustQueue的add
操作就是将请求添加到请求队列中。
// 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;
}
- 如果加入的请求没有被缓存过,就直接加入到消息队列。直接加入不用获取消息队列的锁。因为消息队列是个
BlockingQueue
,本就支持并发操作。而且即使add
操作是在UI线程,也不会阻塞UI线程,因为
mNetworkQueue.add(request);
内部是调用BlockingQueue
的offer
操作,offer
入队操作不会阻塞线程,如果入队失败,它会返回false
。
以上只是我分析的Volley的RequestQueue的大概执行过程。其中还有
CacheQueue
WaitRequests
CurrentRequests
等一些细节和HurlStack和HttpClientStack的网络请求部分没有具体分析。
如果有哪里不对的,希望指正。
网友评论