@946898963
2018-05-18T11:23:18.000000Z
字数 8202
阅读 1154
Android控件跟框架
Android源码分析
HttpStack是一个接口,只有一个方法。主要用于请求网络数据,并返回结果。
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?
在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提供了三个构造函数,如下:
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类中使用的构造函数,但其实最终调用的都是
public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}
默认这两个类都是为null的,但是如果我们要实现对Url的拦截,对url进行一些处理的话,或者利用Https来保证数据传输的安全性的话,我们就可以传入自己实现的UrlRewriterc对象,或者添加SSlSocketFactory。不过在我们一般的项目中,一般用不到,只要稍微了解一下就好。
接下来,看最主要的performRequest方法:
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的超时,缓存,让网络资源写入等属性。
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内容。
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了。
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
5)获得Response的流,并将其解析成对应的HttpEntity对象,设置给Response.entity字段,返回给BasicNetwork。
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访问网络,使用HttpURLConnection还是HttpClient?
参考链接:
Android中关于Volley的使用(九)认识HurlStack(HttpClientStack)
volley源码解析(六)--HurlStack与HttpClientStack之争
Android中的volley_3_网络请求HttpStack、HttpClientStack和HurlStack