以StringRequest为例子,分析Volley是怎样执行一个网络请求的。

先看实现Request抽象类的StringRequest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
@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封装了具体的请求内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 负责构造最终的结果。

一个Volley RequestQueue的创建到运行

RequestQueue的创建

Volley.newRequestQueue有几个重载方法,最终都会执行这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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;
}

其中执行了一些对象的创建工作:

  1. 创建缓存文件,文件名默认为volley
  2. 创建一个UserAgent字符串,代表HTTP头里的客户端身份。默认为包名+APPb版本号。
  3. 创建执行网络请求的工具。HurlStack(Android 2.3及以上)或HttpClientStack(Android 2.3以下)。Stack负责真正的HTTP请求。HurlStack使用的是HttpURLConnection;HttpClientStack使用的是HttpClient
  4. 创建一个Netwoker对象。Netwoker通过调用Stack进行网络访问,并将执行结果封装为NetworkResponse对象。
  5. 创建一个RequestQueue对象,同时创建一个DiskBasedCache缓存对象,作为本地缓存。
  6. RequestQueue创建完毕之后,就调用queue.start()开始不断执行添加到RequestQueue中的请求。

RequestQueue的创建:

RequestQueue有3个构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//最终调用
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数组,实际就是创建了一个线程数组。

1
2
3
4
5
6
7
8
9
10
11
12
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方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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);
}
}
}
  1. 设置当前线程的优先级为后台线程
  2. 从阻塞队列中获取一个request请求。这里使用的take方法,这个方法会阻塞线程,直到线程从队列中拿到了东西。
  3. 给request添加network-queue-take标记
  4. 调用netwoke的performRequest方法,并传入requset获取请求的结果networkResponse
  5. 给request添加network-http-complete标记
  6. 通过response判断是否是304状态码,如果是就调用request.finish(),并跳过下面步骤。否则继续下面的步骤。
  7. 使用request.parseNetworkResponse(networkResponse);创建一个Response对象response
  8. 给request添加network-parse-complete标记
  9. 将请求requset和结果response写入缓存。
  10. 调用request.markDelivered();表明,当前请求已被解决
  11. 调用mDelivery.postResponse(request, response);将请求和结果传递到UI线程。

这就是一个NetworkDispatcher线程执行一个Request的大致流程。

ResponseDelivery->ExecutorDelivery

NetworkDispatcher线程中,最终结果是通过mDeliery这个ResponseDelivery对象传递到UI线程的。在创建NetworkDispatcher时,mDeliery被赋予的实际是ExecutorDelivery的实例。ExecutorDeliveryResponseDelivery接口的实现类。

ExecutorDelivery类的postResponse方法:

1
2
3
4
5
6
7
8
9
10
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 方法调用了 mResponsePosterexecute()方法,并传入了一个Runnable对象。

mResponsePoster 对象是一个 Executor对象,并在 ExecutorDelivery 并创建时就创建。它的execute方法,就是调用 RequestQueue在创建 ExecutorDelivery 传入的拥有UI线程的Looper的handlerpost(Runnable)方法。

1
2
3
4
5
6
7
8
9
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方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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线程执行了:

1
2
3
4
5
6
7
8
9
10
11
12
13
//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操作就是将请求添加到请求队列中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 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);内部是调用BlockingQueueoffer操作,offer入队操作不会阻塞线程,如果入队失败,它会返回false

以上只是我分析的Volley的RequestQueue的大概执行过程。其中还有CacheQueue WaitRequests CurrentRequests等一些细节和HurlStack和HttpClientStack的网络请求部分没有具体分析。
如果有哪里不对的,希望指正。



Android      多线程 Source Code 网络

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!