@946898963
2018-05-18T03:23:18.000000Z
字数 8202
阅读 1444
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());//默认为nullmap.putAll(additionalHeaders);//添加头部,主要是缓存相关的头部信息if (mUrlRewriter != null) {...//代码不执行}URL parsedUrl = new URL(url);HttpURLConnection connection = openConnection(parsedUrl, request);//打开Connectionfor (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 HTTPSif ("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