Volley 源码解析

Volley 源码解析

开始之前

目前已经有很多文章写volley源码解析的了,

为什么这么多呢? 可能是因为volley 的源码相对来说比较少, 逻辑相对简单,好读。

为什么我还要写volley源码解析呢? 和上面的问题的原因一样, 还有就是我也读了好几遍volley的源码了,每次读完后过段时间就会忘记一些,应了那句老话,“好记性不如烂笔头”,现在也不用纸笔了,直接电脑上敲出来,更方便。 我不一定写的比其他同学的高明,但是我会尽量写出我的理解,和现实工作内容联系起来书写。

注意本文是基于volley最新版本 1.1版本源码

本文的目录结构是:

  • volley 的基本使用
  • volley 源码解析
  • volley 的优缺点
  • volley 的扩展

volley 基本使用

Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架。在 Google I/O 2013 大会上发布。

特别适合数据量小,通信频繁的网络操作

1
2
3
4
5
在项目的gradle 文件中添加依赖
dependencies {
...
implementation 'com.android.volley:volley:1.1.1'
}

使用起来很简单,举一个StringRequest例子,其他的类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

//构建一个请求队列
RequestQueue queue = Volley.newRequestQueue(this);
String url ="http://www.baidu.com";
//创建一个request
StringRequest stringRequest = new StringRequest(com.android.volley.Request.Method.GET,url, new com.android.volley.Response.Listener<String>() {
@Override
public void onResponse(String response) {

}
}, new com.android.volley.Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {

}
});

//把request 添加到 队列中
queue.add(stringRequest);

volley源码架构图

volley 源码解析

首先我们看下请求队列的创建。

Volley

1
RequestQueue queue = Volley.newRequestQueue(this);
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

public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (BaseHttpStack) null);
}

重载 newRequestQueue 方法

public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
BasicNetwork network;

if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
network = new BasicNetwork(new HurlStack());
} else {
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info =
context.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}

network =
new BasicNetwork(
new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
}
} else {
network = new BasicNetwork(stack);
}

return newRequestQueue(context, network);
}



最终调用这个方法start
private static RequestQueue newRequestQueue(Context context, Network network) {
//创建缓存目录
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
//创建任务队列
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
//启动队列
queue.start();
return queue;
}

先判断是否有指定 stack 也就是实际请求的方式,如果有传进来,就用传的来指定的方式,
如果为空, 就按照sdk 版本创建相应的请求方式, sdk版本<9的使用 httpClient 请求

大于等于9的使用 httpUrlconnection 来请求,这是因为,httpUrlconnection在Android 2.2之前有bug。但是httpClient 没有 httpUrlconnection 性能好, Api简单,体积较小,压缩和缓存机制也可有效减少网络访问的流量, 而且httpclient 请求的方式 在Android6.0之后也直接从sdk 中直接去掉了。

RequestQueue

1
2
3
4
5
6
7
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(
cache,
network,
threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}

可以重点关注 ExecutorDelivery(new Handler(Looper.getMainLooper())))
接下来在线程切换地方法讲到它,这个是成功把子线程数据发送到主线程的关键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
任务队列的启动方法
/** Starts the dispatchers in this queue. */
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
//创建了一个缓存调度线程 并启动
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();

// Create network dispatchers (and corresponding threads) up to the pool size.
//创建了4个网络调度线程,并启动
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}

任务队列创建完成,等到request 加入,request的创建比较简单,可以根据自己的需求创建相应的request ,系统提供了 String ,Image, JsonObject ,jsonArray 等request,他们的区别主要是在 parseNetworkResponse 方法中根据不同的数据类型,进行相应数据类型的解析。

再看网络请求的 add 方法,就是把request 添加到 任务队列中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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");

// 判断这个 request 是否支持缓存, 如果不支持就直接 把这个request加入到网络调度线程中
//否则就加入到缓存 调度线程。
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
mCacheQueue.add(request);
return request;
}

这个 mNetworkQueue 和 mCacheQueue 都是 PriorityBlockingQueue 优先级阻塞队列 现在根据情况分别加入到相应的调度线程等待执行

CacheDispatcher

上面已经把request加入到缓存队列中了,接下来看下 CacheDispatcher 是如何处理这些request的。

1
2
3
4

public class CacheDispatcher extends Thread {
...
}

能看到这个 CacheDispatcher 继承 Thread ,前面我们也看到start 方法中已经 调用了线程的 start() ,那我们看下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

@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

// Make a blocking call to initialize the cache.
mCache.initialize();

while (true) {
try {
processRequest();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
Thread.currentThread().interrupt();
return;
}
VolleyLog.e(
"Ignoring spurious interrupt of CacheDispatcher thread; "
+ "use quit() to terminate it");
}
}
}

能看到代码相当简洁, 就是一个 while (true) 死循环, 里面执行 processRequest方法,

1
2
3
4
5
6
private void processRequest() throws InterruptedException {
// Get a request from the cache triage queue, blocking until
// at least one is available.
final Request<?> request = mCacheQueue.take();
processRequest(request);
}

英文注释写的也特别清楚,就是从 BlockingQueue阻塞队列中取出,如果有数据就处理,没有数据就阻塞在这。

BlockingQueue

如果BlockQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒。

同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间才会被唤醒继续操作。

了解了 BlockingQueue 我们看下真正处理request的详细方法

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
67
68
69
70
71
72
73

void processRequest(final Request<?> request) throws InterruptedException {
request.addMarker("cache-queue-take");

// 先判断这个request是否已经被取消,需求就不继续了,直接调用finish 方法
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
return;
}

// 尝试从缓存中查找是否有同样的request在
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// 如果没有缓存,就加入到网络请求的队列
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}

//如果缓存已经过期,也加入到网络请求的队列
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}

// 在缓存中找到了这个请求,且没有过期,把 服务端返回的数据解析后取出,传回。

request.addMarker("cache-hit");
Response<?> response =
request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");

if (!entry.refreshNeeded()) {
// 直接发送
mDelivery.postResponse(request, response);
} else {
// 发送缓存的同时 也把request加入到网络请求线程,刷新数据
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;

if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
// 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) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
});
} else {
// request has been added to list of waiting requests
// to receive the network response from the first request once it returns.
mDelivery.postResponse(request, response);
}
}
}

NetworkDispatcher

看完了 缓存队列的执行方式, 再看下网络请求线程的执行,跟上面的方法方式类似,我们直奔processRequest看它是如何实现的。

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
void processRequest(Request<?> request) {
long startTimeMs = SystemClock.elapsedRealtime();
try {
request.addMarker("network-queue-take");

// 是否取消,取消的话就结束

if (request.isCanceled()) {
request.finish("network-discard-cancelled");
request.notifyListenerResponseNotUsable();
return;
}

addTrafficStatsTag(request);

// 进行网络请求, performRequest 这个方法中是具体的网络请求的内容
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");


// 如果返回304 同时已经回传过了返回值,就不再回传内容了,结束
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
request.notifyListenerResponseNotUsable();
return;
}

// 在工作线程中解析返回内容
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");
}

// 把解析出的内容回传。
request.markDelivered();
mDelivery.postResponse(request, response);
request.notifyListenerResponseReceived(response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
request.notifyListenerResponseNotUsable();
} 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.notifyListenerResponseNotUsable();
}
}

具体执行网络请求的是 BasicNetwork 的 performRequest 方法

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
List<Header> responseHeaders = Collections.emptyList();
try {
// 把所有的header 信息拿到
Map<String, String> additionalRequestHeaders =
getCacheHeaders(request.getCacheEntry());
// 具体的根据不同的情况,看Android SDK 版本号 是否小于9 来选择使用
// httpClient 还是httpUrlconnection 进行创建网络链接
httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
int statusCode = httpResponse.getStatusCode();

responseHeaders = httpResponse.getHeaders();
// 处理304问题
if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(
HttpURLConnection.HTTP_NOT_MODIFIED,
/* data= */ null,
/* notModified= */ true,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
}
// Combine cached and response headers so the response will be complete.
List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);
return new NetworkResponse(
HttpURLConnection.HTTP_NOT_MODIFIED,
entry.data,
/* notModified= */ true,
SystemClock.elapsedRealtime() - requestStart,
combinedHeaders);
}

// 处理异常情况
InputStream inputStream = httpResponse.getContent();
if (inputStream != null) {
responseContents =
inputStreamToBytes(inputStream, httpResponse.getContentLength());
} 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, statusCode);

if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
//最后new 了一个NetworkResponse 把返回的内容包装后返回前一个方法,继续处理
return new NetworkResponse(
statusCode,
responseContents,
/* notModified= */ false,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
} catch (SocketTimeoutException e) {
//重试机制
attemptRetryOnException("socket", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
int statusCode;
if (httpResponse != null) {
statusCode = httpResponse.getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
NetworkResponse networkResponse;
if (responseContents != null) {
networkResponse =
new NetworkResponse(
statusCode,
responseContents,
/* notModified= */ false,
SystemClock.elapsedRealtime() - requestStart,
responseHeaders);
if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED
|| statusCode == HttpURLConnection.HTTP_FORBIDDEN) {
//重试机制
attemptRetryOnException(
"auth", request, new AuthFailureError(networkResponse));
} else if (statusCode >= 400 && statusCode <= 499) {
// Don't retry other client errors.
throw new ClientError(networkResponse);
} else if (statusCode >= 500 && statusCode <= 599) {
if (request.shouldRetryServerErrors()) {
attemptRetryOnException(
"server", request, new ServerError(networkResponse));
} else {
throw new ServerError(networkResponse);
}
} else {
// 3xx? No reason to retry.
throw new ServerError(networkResponse);
}
} else {
//重试机制
attemptRetryOnException("network", request, new NetworkError());
}
}
}
}

HurlStack

我们可以再看下建立网络链接的部分,这个是httpUrlcinnection,

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

@Override
public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<>();
map.putAll(additionalHeaders);
// Request.getHeaders() takes precedence over the given additional (cache) headers).
map.putAll(request.getHeaders());
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
boolean keepConnectionOpen = false;
try {
for (String headerName : map.keySet()) {
connection.setRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}

if (!hasResponseBody(request.getMethod(), responseCode)) {
return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields()));
}

// Need to keep the connection open until the stream is consumed by the caller. Wrap the
// stream such that close() will disconnect the connection.
keepConnectionOpen = true;
return new HttpResponse(
responseCode,
convertHeaders(connection.getHeaderFields()),
connection.getContentLength(),
new UrlConnectionInputStream(connection));
} finally {
if (!keepConnectionOpen) {
connection.disconnect();
}
}
}

总结上面这个方法就是 把所有的请求头信息发送出去,如果有请求体,把请求体也发送过去, 等待服务端相应,响应后把返回值回传到上一个方法中处理。

volley 是如何实现线程切换的

  1. 主线程上开启子线程执行网络请求在上面分析中能看到。 创建了1个缓存线程,4个网络请求线程,执行相应的网络请求。
  2. 子线程在处理完耗时操作,处理完数据,怎么发送到主线程的呢?
ExecutorDelivery

接下来看下我们上面多次看到的 mDelivery.postResponse(request, response); 是怎么处理的

先看下构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    public ExecutorDelivery(final Handler handler) {
//注意这个 Handler 这是RequestQueue 构造方法中初始化的,传入的是 Looper.getMainLooper(), 它就是主线程的handler
//创建了一个线程池,它的作用就是在线程中调用 handler,发送 runnable,通过这种形式使得 runnable 在 UI 线程中执行。
mResponsePoster =
new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}


//看下具体方法
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}

通过这种方式,mResponsePoster.execute 是得里面的runnable能够在主线程中得到执行,

ResponseDeliveryRunnable
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
    // 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");
}

// 发送之后,都会调用 Runnable 的 run() ,这个已经可执行的 Runnable 已经切换到主线程了。
if (mRunnable != null) {
mRunnable.run();
}
}

最后调用 request 的 finish 方法,表示该请求已经执行结束了,同时,如果 ResponseDeliveryRunnable 的构造方法中的第三个参数 runnable 不为空,立即执行该 runnable 的 run 方法。

再看这个Stringrequest中的

1
2
3
4
5
6
7
8
9
10
11

@Override
protected void deliverResponse(String response) {
Response.Listener<String> listener;
synchronized (mLock) {
listener = mListener;
}
if (listener != null) {
listener.onResponse(response);
}
}

在主线程 通过接口回调,回调到发起请求的位置,进行相应的处理。

volley 的优缺点

优点:

  • 特别适合数据量小,通信频繁的网络操作
  • 轻量,jar包相对较小
  • 扩展性强。Volley 中大多是基于接口的设计,可配置性强。
  • 一定程度符合 Http 规范,包括返回 ResponseCode(2xx、3xx、4xx、5xx)的处理,请求头的处理,缓存机制的支持等。并支持重试及优先级定义。
  • 默认 Android2.3 及以上基于 HttpURLConnection,2.3 以下基于 HttpClient 实现,
  • 提供简便的图片加载工具。
  • 网络请求线程NetworkDispatcher默认开启了4个,可以优化,通过手机CPU数量

缺点:

  • 在BasicNetwork中判断了statusCode(statusCode < 200 || statusCode > 299),如何符合条件直接抛出IOException(),不够合理。
  • 图片加载性能一般
  • 导致401等其他状态抛出IOException
  • 对大文件下载 Volley的表现非常糟糕
  • 使用的是httpclient,HttpURLConnection。不过在android 6.0不支持httpclient了,如果想支持得添加org.apache.http.legacy.jar

为什么volley 不适合下载上传大文件?为什么适合数据量小的频率高的请求?

  1. Volley的网络请求线程池默认大小为4。意味着可以并发进行4个请求,大于4个,会排在队列中。
  2. Volley将整个response加载到内存并进行操作(可以是解析等操作)大文件可能会引起OOM

volley 的扩展

有时候我们整个项目在使用的volley 如果要替换成okhttp 的成本就比较高了, 那能不能方便简单的时候okhttp 的优秀的功能呢, 答案当然是可以的。

还记得这个吗? 最开始分析源码时,

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

public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
BasicNetwork network;

if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
network = new BasicNetwork(new HurlStack());
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
// At some point in the future we'll move our minSdkVersion past Froyo and can
// delete this fallback (along with all Apache HTTP code).
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info =
context.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}

network =
new BasicNetwork(
new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
}
} else {
network = new BasicNetwork(stack);
}

return newRequestQueue(context, network);
}

如果我们传stack 进来,这个 stack 就是null 了,所以,我们只需要实现这个HttpStack 然后传进来就行了。

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145


public class OkHttpStack implements HttpStack {
private final OkHttpClient mClient;
//提供一个构造函数,传入OkHttpClient 对象
public OkHttpStack(OkHttpClient client) {
this.mClient = client;
}

private static HttpEntity entityFromOkHttpResponse(Response response) throws IOException {
BasicHttpEntity entity = new BasicHttpEntity();
ResponseBody body = response.body();

entity.setContent(body.byteStream());
entity.setContentLength(body.contentLength());
entity.setContentEncoding(response.header("Content-Encoding"));

if (body.contentType() != null) {
entity.setContentType(body.contentType().type());
}
return entity;
}

//请求方法和请求体
@SuppressWarnings("deprecation")
private static void setConnectionParametersForRequest
(okhttp3.Request.Builder builder, Request<?> request)
throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Request.Method.DEPRECATED_GET_OR_POST:
byte[] postBody = request.getPostBody();
if (postBody != null) {
builder.post(RequestBody.create
(MediaType.parse(request.getPostBodyContentType()), postBody));
}
break;

case Request.Method.GET:
builder.get();
break;

case Request.Method.DELETE:
builder.delete();
break;

case Request.Method.POST:
builder.post(createRequestBody(request));
break;

case Request.Method.PUT:
builder.put(createRequestBody(request));
break;

case Request.Method.HEAD:
builder.head();
break;

case Request.Method.OPTIONS:
builder.method("OPTIONS", null);
break;

case Request.Method.TRACE:
builder.method("TRACE", null);
break;

case Request.Method.PATCH:
builder.patch(createRequestBody(request));
break;

default:
throw new IllegalStateException("Unknown method type.");
}
}

private static RequestBody createRequestBody(Request request) throws AuthFailureError {
final byte[] body = request.getBody();
if (body == null) return null;

return RequestBody.create(MediaType.parse(request.getBodyContentType()), body);
}

private static ProtocolVersion parseProtocol(final Protocol protocol) {
switch (protocol) {
case HTTP_1_0:
return new ProtocolVersion("HTTP", 1, 0);
case HTTP_1_1:
return new ProtocolVersion("HTTP", 1, 1);
case SPDY_3:
return new ProtocolVersion("SPDY", 3, 1);
case HTTP_2:
return new ProtocolVersion("HTTP", 2, 0);
}

throw new IllegalAccessError("Unkwown protocol");
}
//performRequest方法,首先我们设置一下超时时间
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
int timeoutMs = request.getTimeoutMs();
OkHttpClient client = mClient.newBuilder()
.readTimeout(timeoutMs, TimeUnit.MILLISECONDS)
.connectTimeout(timeoutMs, TimeUnit.MILLISECONDS)
.writeTimeout(timeoutMs, TimeUnit.MILLISECONDS)
.build();

okhttp3.Request.Builder okHttpRequestBuilder = new okhttp3.Request.Builder();
//设置请求头,请求头的来源有两个
Map<String, String> headers = request.getHeaders();
for (final String name : headers.keySet()) {
okHttpRequestBuilder.addHeader(name, headers.get(name));
}

for (final String name : additionalHeaders.keySet()) {
okHttpRequestBuilder.addHeader(name, additionalHeaders.get(name));
}

setConnectionParametersForRequest(okHttpRequestBuilder, request);

okhttp3.Request okhttp3Request = okHttpRequestBuilder.url(request.getUrl()).build();
//开始请求
Response okHttpResponse = client.newCall(okhttp3Request).execute();

StatusLine responseStatus = new BasicStatusLine
(
parseProtocol(okHttpResponse.protocol()),
okHttpResponse.code(),
okHttpResponse.message()
);

BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromOkHttpResponse(okHttpResponse));

Headers responseHeaders = okHttpResponse.headers();
for (int i = 0, len = responseHeaders.size(); i < len; i++) {
final String name = responseHeaders.name(i), value = responseHeaders.value(i);
if (name != null) {
response.addHeader(new BasicHeader(name, value));
}
}
//最后返回HttpResponse对象
return response;
}

}

总结

总体来看,volley 源码还是比较简单的,结构清晰,代码量少,对于想读源码的同学还是一个比较不错的选择。有兴趣的可以尝试下。 也希望看这篇文章的同学能从本文中受益,也欢迎与我交流学习。

下一篇是分析 okHttp 的源码,敬请期待。

请联系我!