volley源代码解析(六)--HurlStack与HttpClientStack之争
Volley中网络载入有两种方式,各自是HurlStack与HttpClientStack。我们来看Volley.java中的一段代码
if (stack == null) {//假设没有限定stack
if (Build.VERSION.SDK_INT >= 9) {//adk版本号在9或者以上
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
由此可见,假设没有设置stack,则依据当前adk版本号自己主动选择。在Android 2.2版本号之前,HttpClient拥有较少的bug。因此使用它是最好的选择。
而在Android 2.3版本号及以后。HttpURLConnection则是最佳的选择。
它的API简单。体积较小,因而很适用于Android项目。压缩和缓存机制能够有效地降低网络訪问的流量,在提升速度和省电方面也起到了较大的作用。对于新的应用程序应该更加偏向于使用HttpURLConnection,由于在以后的工作其中我们也会将很多其它的时间放在优化HttpURLConnection上面。
为此,我们须要分别来看这两个类。在看这两个之前,我们先来看它们一个简单的父类HttpStack
/**
* An HTTP stack abstraction.
* 抽象的http栈
*/
public interface HttpStack {
/**
* Performs an HTTP request with the given parameters.
* 依据參数。运行http请求
* <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
* and the Content-Type header is set to request.getPostBodyContentType().</p>
*
* @param request the request to perform
* @param additionalHeaders additional headers to be sent together with
* {@link Request#getHeaders()}
* @return the HTTP response
*/
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError; }
该父类主要规定了,子类必须有一个依据request请求数据,而且返回HttpResponse类的方法
OK,接下来我们先看HurlStack,这个类使用的是HttpURLConnection作为连接方式,在adk较高版本号推荐使用(事实上眼下市场上2.3的系统已经非常少见了)
我们直接看这个类的核心方法performRequest()
@Override
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());
map.putAll(additionalHeaders);
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);//开启连接
for (String headerName : map.keySet()) {//加入请求參数
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);//设置请求方式
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);//http协议
int responseCode = connection.getResponseCode();//获取响应状态
if (responseCode == -1) {//-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.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;
}
整个方法分成几个步骤,首先是将请求參数。存储到map其中
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
然后是开启url连接
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);//开启连接
来看openConnection()方法
/**
* Opens an {@link HttpURLConnection} with parameters.
* 开启网络连接
* @param url
* @return an open connection
* @throws IOException
*/
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) {//https
((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
} return connection;
} /**
* Create an {@link HttpURLConnection} for the specified {@code url}.
*/
protected HttpURLConnection createConnection(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
这种方法主要就是调用url.openConnevtion()从而返回一个HttpURLConnection对象。当中的一些超时设置,是由request本身提供的
另外还依据url是否带有https。为HttpURLConnection设置setSSLSocketFactory(mSslSocketFactory对象是在构造方法中传入的)
得到HttpURLConnection,就设置请求參数
for (String headerName : map.keySet()) {//加入请求參数
connection.addRequestProperty(headerName, map.get(headerName));
}
然后是确定请求方式(GET,POST还是别的)
setConnectionParametersForRequest(connection, request);//设置请求方式
setConnectionParametersForRequest方法:
@SuppressWarnings("deprecation")
/**
* 设置请求方式
* @param connection
* @param request
* @throws IOException
* @throws AuthFailureError
*/
/* package */
static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST:
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
byte[] postBody = request.getPostBody();
if (postBody != null) {
// Prepare output. There is no need to set Content-Length explicitly,
// since this is handled by HttpURLConnection using the size of the prepared
// output stream.
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:
// Not necessary to set the request method because connection defaults to GET but
// being explicit here.
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.");
}
}
最后获取响应。将响应头信息包装成StatusLine对象,再包装成BasicHttpResponse对象
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());//响应状态类
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
然后为BasicHttpResponse增加响应内容
response.setEntity(entityFromConnection(connection));//解析响应实体
entityFromConnection(HttpURLConnection connection)方法:
/**
* Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
* <br>解析出响应实体
* @param connection
* @return an HttpEntity populated with data from <code>connection</code>.
*/
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;
}
最后,增加响应头部内容
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);
}
}
OK。这样就返回了一个具有完整信息的HttpResponse对象。整个过程比較简单,是常规的网络请求内容。
接下来我们看HttpClientStack的实现
相同。直接来看performRequest()方法
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
addHeaders(httpRequest, additionalHeaders);//加入缓存头
addHeaders(httpRequest, request.getHeaders());//加入请求头
onPrepareRequest(httpRequest);//请求预处理
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
return mClient.execute(httpRequest);
}
请求步骤,首先是依据请求方式。构造HttpUriRequest对象,而且设置请求參数
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
createHttpRequest()方法:
/**
* Creates the appropriate subclass of HttpUriRequest for passed in request.
* 依据请求方式返回相应HttpUriRequest的子类
*/
@SuppressWarnings("deprecation")
/* protected */
static HttpUriRequest createHttpRequest(Request<?> request,
Map<String, String> additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST: {
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
byte[] postBody = request.getPostBody();
if (postBody != null) {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
HttpEntity entity;
entity = new ByteArrayEntity(postBody);
postRequest.setEntity(entity);
return postRequest;
} else {
return new HttpGet(request.getUrl());
}
}
case Method.GET:
return new HttpGet(request.getUrl());
case Method.DELETE:
return new HttpDelete(request.getUrl());
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request);//设置请求參数
return postRequest;
}
case Method.PUT: {
HttpPut putRequest = new HttpPut(request.getUrl());
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(putRequest, request);
return putRequest;
}
case Method.HEAD:
return new HttpHead(request.getUrl());
case Method.OPTIONS:
return new HttpOptions(request.getUrl());
case Method.TRACE:
return new HttpTrace(request.getUrl());
case Method.PATCH: {
HttpPatch patchRequest = new HttpPatch(request.getUrl());
patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(patchRequest, request);
return patchRequest;
}
default:
throw new IllegalStateException("Unknown request method.");
}
}
从createHttpRequest()方法能够看出。在HttpClient中,仅仅要依据请求方式,new一个HttpGet/HttpPost/....对象就能够了(而urlstack这一步是真的connnection而言的)
接着是为HttpUriRequest对象设置请求头部
addHeaders(httpRequest, additionalHeaders);//加入缓存头
addHeaders(httpRequest, request.getHeaders());//加入请求头
addHeaders方法:
/**
* 加入响应头
* @param httpRequest
* @param headers
*/
private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
for (String key : headers.keySet()) {
httpRequest.setHeader(key, headers.get(key));
}
}
最后,将HttpUriRequest对象交给httpClient运行
return mClient.execute(httpRequest);
OK,HttpClientStack比我们想象的还要简单。起码比HurlStack简单,这是当然的。由于使用httpClient方式,其本质就是对urlConnection的封装,然而这个封装并非非常完美。所以造成了版本号之间的差异。
到此为止,给大家介绍了HurlStack与HttpClientStack这两个类,同一时候也说明了真正的网络请求在哪里运行。
下一篇文章,将会来了解Response<T>的使用,Response<T>是Volley整个过程中。辗转获得的终于目的,作为响应实体。我们来看一下Response<T>是怎么设计的。
volley源代码解析(六)--HurlStack与HttpClientStack之争的更多相关文章
- volley源代码解析(七)--终于目的之Response<T>
在上篇文章中,我们终于通过网络,获取到了HttpResponse对象 HttpResponse是android包里面的一个类.然后为了更高的扩展性,我们在BasicNetwork类里面看到.Volle ...
- Android:Volley源代码解析
简单实例 Volley是一个封装HttpUrlConnection和HttpClient的网络通信框架,集AsyncHttpClient和Universal-Image-Loader的长处于了一身.既 ...
- Tomcat类载入器机制(Tomcat源代码解析六)
要说Tomcat的Classloader机制,我们还得从Bootstrap開始.在BootStrap初始化的时候.调用了org.apache.catalina.startup.Bootstrap#in ...
- Spring源代码解析
Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.itey ...
- Spring源代码解析(收藏)
Spring源代码解析(收藏) Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的 ...
- Tomcat源代码解析系列
学web也有一段时间了.为了从底层了解web应用在Tomcat中的执行,决定看一下Tomcat的源代码參见<How Tomcat works> 和大牛博客.对大体架构有了一定的了解, ...
- Android Volley全然解析(四),带你从源代码的角度理解Volley
版权声明:本文出自郭霖的博客,转载必须注明出处. https://blog.csdn.net/sinyu890807/article/details/17656437 转载请注明出处:http://b ...
- Android源代码解析之(六)-->Log日志
转载请标明出处:一片枫叶的专栏 首先说点题外话,对于想学android framework源代码的同学,事实上能够在github中fork一份,详细地址:platform_frameworks_bas ...
- Android Volley完全解析
1. Volley简介 我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据.Android系统中主要提供了两种方式来进行H ...
随机推荐
- [Node.js] Proxy Requests for Local and Remote Service Parity
High availability apps require that no distinction be made between local and remote services. Attach ...
- 金蝶KIS标准版与金蝶K3的差别
一.数据库 金蝶KIS标准版使用MS Access数据库.该数据库适用于小规模的数据处理,是比較经济的数据库解决方式,但当单个表的数据记录超过5万条时.执行的速度和稳定性都将受到一定程序的影响. K ...
- node-webkit 屏幕截图功能
做 IM 屏幕截图是少不了的,之前 windows 版本是调用的 qq 输入法的截图功能,这个版本又再次尝试自己实现发现是可以的,getusermedia 的权限很高,代码如下 <!DOCTYP ...
- iOS_6_ToolBar+xib+红楼梦
终于效果图 BeyondViewController.h // // BeyondViewController.h // 6_ToolBar // // Created by beyond on 14 ...
- java连接mysql数据库增删改查操作记录
1. 连接数据库.得到数据库连接变量 注意连接数据库的时候 (1)打开DB Browser 新建一个Database Driver,注意加入Driver JARs的时候加入的包,我的是mysql-co ...
- [yueqian_scut]Android多点触控技术和应用框架
Android多点触控技术跟Linux输入子系统紧密相关.本文将从应用的角度说明Android多点触控技术的接口和应用. 一.多点触控场景分析 网络上有关Android多点触控技术的文章多见于两点拉伸 ...
- 粘性固定属性 -- position:sticky
概述 position: sticky,这是一个比较容易忽略的css3 position 新属性,它的作用即为实现粘性布局,它是 relative 与 fixed 的结合. 用法 默认情况下,其表现为 ...
- windows服务更改配置文件
现场部署的服务所在文件夹内容如上图所示,由于现场数据库服务器更改了IP,所以我服务里的数据库连接字符串也需要修改(注意到日志文件,从某天改了IP后就再也连不上数据库了) 修改过程: 1.打开服务管理, ...
- 后缀自动机 (WJMZBMR讲稿的整理和注释)
链接放在这里,有点难理解,至少我个人是的. 后缀自动机是一种有限状态自动机,其功能是识别字符串是否是母串的后缀.它能解决的问题当然不仅仅是判断是不是后缀这种事,跟字符串的连续子串有关的问题都可以往这个 ...
- 【摘录】JAVA内存管理-有关垃圾收集的关键参数
第八章 有关垃圾收集的关键参数 一些命令行参数可以用来选择垃圾收集器,指定堆或代的大小,修改垃圾收集行为,获取垃圾收集统计数据.本章给出一些最常用的参数.有关各种各样参数更多完整的列表和详细信息可以参 ...