Volley-HttpStack及其实现类源码解析

HttpStack是一个接口,只有一个方法。主要用于请求网络数据,并返回结果。

1
2
3
4
public interface HttpStack {
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}

此处输入图片的描述

HttpStack有两个实现类,HurlStack和HttpClientStack,HurlStack是利用HttpUrlConnection实现的,HttpClientStack是利用HttpClient实现的。

在Volley的newRequestQueue(Context context, HttpStack stack)方法中,如果用户传入的HttpStack为空的话,会自动的根据当前系统的版本来创建HttpStack的实现类,如果SDK大于等于9,也就是Android 2.3以后,会创建HurlStack,否则会创建HttpClientStack。

关于HurlStack和HttpClientStack的优劣的问题,建议阅读:Android访问网络,使用HttpURLConnection还是HttpClient?

1
2
3
在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。

而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。对于新的应用程序应该更加偏向于使用HttpURLConnection,因为在以后的工作当中我们也会将更多的时间放在优化HttpURLConnection上面。

若用户想要使用其他的网络请求类库,比如okhttp等就可以实现HttpStack接口,并在performRequest方法中调用okhttp进行网络请求,并把请求的结果封装成一个HttpResponse返回即可,HttpResponse中包含了状态码,响应头,body信息。

HttpStack的两个子类HurlStack&HttpClientStack。由于HurlStack和HttpClientStack的实现机制是一样的,只是使用的类不一样,我们这篇文章就只讲解HurlStack了。

HurlStack提供了三个构造函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public HurlStack() {  
this(null);
}

/**
* @param urlRewriter Rewriter to use for request URLs
*/
public HurlStack(UrlRewriter urlRewriter) {
this(urlRewriter, null);
}

/**
* @param urlRewriter Rewriter to use for request URLs
* @param sslSocketFactory SSL factory to use for HTTPS connections
*/
public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}

其中第一个就是Volley类中使用的构造函数,但其实最终调用的都是

1
2
3
4
public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {  
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}

默认这两个类都是为null的,但是如果我们要实现对Url的拦截,对url进行一些处理的话,或者利用Https来保证数据传输的安全性的话,我们就可以传入自己实现的UrlRewriterc对象,或者添加SSlSocketFactory。不过在我们一般的项目中,一般用不到,只要稍微了解一下就好。
接下来,看最主要的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
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)  
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());//默认为null
map.putAll(additionalHeaders);//添加头部,主要是缓存相关的头部信息
if (mUrlRewriter != null) {
...//代码不执行
}
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);//打开Connection
for (String headerName : map.keySet()) {
//将Map的对象添加到Connection的属性中
connection.addRequestProperty(headerName, map.get(headerName));
}
//设置connection方法,主要是设置Method属性和Content(for post/put)
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection. ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);//Http 1.1 协议
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.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
//将返回的内容解析成response的Entity对象
response.setEntity(entityFromConnection(connection));
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}

HttpURLConnection是Android3.0以后才提供的一个网络访问类,而HurlStack类,也正是H(ttp)URL的缩写,所以这个类,其实就是基于HttpUrlConnection的实现,其步骤如下:
1)从Request中获得url参数,根据url参数构造URL对象,而URL对象是java提供的获取网络资源的一个封装好的实用类。
2)从URL对象打开Connection,并设置connection的超时,缓存,让网络资源写入等属性。

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
private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {  
HttpURLConnection connection = createConnection(url);

int timeoutMs = request.getTimeoutMs();
connection.setConnectTimeout(timeoutMs);
connection.setReadTimeout(timeoutMs);
connection.setUseCaches(false);
connection.setDoInput(true);

// use caller-provided custom SslSocketFactory, if any, for HTTPS
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
}

return connection;
}

protected HttpURLConnection createConnection(URL url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//设置所有的http连接是否自动处理重定向;
// public static void HttpURLConnection.setFollowRedirects(boolean //followRedirects)
//设置本次连接是否自动处理重定向。设置成true,系统自动处理重定向;设置成false则需要自己从http reply中分析新的url自己重新连接。
connection.setInstanceFollowRedirects(HttpURLConnection.getFollowRedirects());

return connection;
}

3)调用方法 setConnectionParametersForRequest来设置Method属性,如果是Post或者Put的话,还要设置Content内容。

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
static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST:
byte[] postBody = request.getPostBody();
if (postBody != null) {
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.addRequestProperty(HEADER_CONTENT_TYPE,
request.getPostBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(postBody);
out.close();
}
break;
case Method.GET:
connection.setRequestMethod("GET");
break;
case Method.DELETE:
connection.setRequestMethod("DELETE");
break;
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
case Method.PUT:
connection.setRequestMethod("PUT");
addBodyIfExists(connection, request);
break;
case Method.HEAD:
connection.setRequestMethod("HEAD");
break;
case Method.OPTIONS:
connection.setRequestMethod("OPTIONS");
break;
case Method.TRACE:
connection.setRequestMethod("TRACE");
break;
case Method.PATCH:
connection.setRequestMethod("PATCH");
addBodyIfExists(connection, request);
break;
default:
throw new IllegalStateException("Unknown method type.");
}
}

private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
throws IOException, AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
connection.setDoOutput(true);
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
}
}

4)设置Http 协议,这里基本上是1.1了。

1
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);

5)获得Response的流,并将其解析成对应的HttpEntity对象,设置给Response.entity字段,返回给BasicNetwork。

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
        int responseCode = connection.getResponseCode();
if (responseCode == -1) {
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}

private static boolean hasResponseBody(int requestMethod, int responseCode) {
return requestMethod != Request.Method.HEAD
&& !(HttpStatus.SC_CONTINUE <= responseCode && responseCode < HttpStatus.SC_OK)
&& responseCode != HttpStatus.SC_NO_CONTENT
&& responseCode != HttpStatus.SC_NOT_MODIFIED;
}

private static HttpEntity entityFromConnection(HttpURLConnection connection) {
BasicHttpEntity entity = new BasicHttpEntity();
InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
entity.setContent(inputStream);
entity.setContentLength(connection.getContentLength());
entity.setContentEncoding(connection.getContentEncoding());
entity.setContentType(connection.getContentType());
return entity;
}

6)BasicNetwork获得返回来的Response对象,就会由Request去解析这个Response对象,因为不同的请求返回来的对象是不一样的,所以这个解析的过程必须由各个请求的实现类自己去实现,也即如ImageRequest,JsonObjectRequest对象等,都要实现自己的parseNetworkResponse方法。

相关HTTP知识:

Http status code : 304
HTTP状态码

HttpUrlConnection相关知识:

关于HttpURLConnection.setFollowRedirects
如何通过HttpURLConnection得到http 302的跳转地址

HttpUrlConnection的setDoOutput与setDoInput的区别
HttpURLConnection用法详解

Android中TrafficStats流量监控类

Android访问网络,使用HttpURLConnection还是HttpClient?

参考链接:

Android中关于Volley的使用(九)认识HurlStack(HttpClientStack)
volley源码解析(六)–HurlStack与HttpClientStack之争
Android中的volley_3_网络请求HttpStack、HttpClientStack和HurlStack

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器