[关闭]
@946898963 2018-05-18T11:23:18.000000Z 字数 8202 阅读 1154

Volley-HttpStack

Android控件跟框架 Android源码分析


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

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

此处输入图片的描述

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

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

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

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

  1. public HurlStack() {
  2. this(null);
  3. }
  4. /**
  5. * @param urlRewriter Rewriter to use for request URLs
  6. */
  7. public HurlStack(UrlRewriter urlRewriter) {
  8. this(urlRewriter, null);
  9. }
  10. /**
  11. * @param urlRewriter Rewriter to use for request URLs
  12. * @param sslSocketFactory SSL factory to use for HTTPS connections
  13. */
  14. public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
  15. mUrlRewriter = urlRewriter;
  16. mSslSocketFactory = sslSocketFactory;
  17. }

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

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

默认这两个类都是为null的,但是如果我们要实现对Url的拦截,对url进行一些处理的话,或者利用Https来保证数据传输的安全性的话,我们就可以传入自己实现的UrlRewriterc对象,或者添加SSlSocketFactory。不过在我们一般的项目中,一般用不到,只要稍微了解一下就好。
接下来,看最主要的performRequest方法:

  1. public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
  2. throws IOException, AuthFailureError {
  3. String url = request.getUrl();
  4. HashMap<String, String> map = new HashMap<String, String>();
  5. map.putAll(request.getHeaders());//默认为null
  6. map.putAll(additionalHeaders);//添加头部,主要是缓存相关的头部信息
  7. if (mUrlRewriter != null) {
  8. ...//代码不执行
  9. }
  10. URL parsedUrl = new URL(url);
  11. HttpURLConnection connection = openConnection(parsedUrl, request);//打开Connection
  12. for (String headerName : map.keySet()) {
  13. //将Map的对象添加到Connection的属性中
  14. connection.addRequestProperty(headerName, map.get(headerName));
  15. }
  16. //设置connection方法,主要是设置Method属性和Content(for post/put)
  17. setConnectionParametersForRequest(connection, request);
  18. // Initialize HttpResponse with data from the HttpURLConnection. ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);//Http 1.1 协议
  19. int responseCode = connection.getResponseCode();
  20. if (responseCode == -1) {
  21. // -1 is returned by getResponseCode() if the response code could not be retrieved.
  22. // Signal to the caller that something was wrong with the connection.
  23. throw new IOException("Could not retrieve response code from HttpUrlConnection.");
  24. }
  25. StatusLine responseStatus = new BasicStatusLine(protocolVersion,
  26. connection.getResponseCode(), connection.getResponseMessage());
  27. BasicHttpResponse response = new BasicHttpResponse(responseStatus);
  28. //将返回的内容解析成response的Entity对象
  29. response.setEntity(entityFromConnection(connection));
  30. for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
  31. if (header.getKey() != null) {
  32. Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
  33. response.addHeader(h);
  34. }
  35. }
  36. return response;
  37. }

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

  1. private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
  2. HttpURLConnection connection = createConnection(url);
  3. int timeoutMs = request.getTimeoutMs();
  4. connection.setConnectTimeout(timeoutMs);
  5. connection.setReadTimeout(timeoutMs);
  6. connection.setUseCaches(false);
  7. connection.setDoInput(true);
  8. // use caller-provided custom SslSocketFactory, if any, for HTTPS
  9. if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
  10. ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
  11. }
  12. return connection;
  13. }
  14. protected HttpURLConnection createConnection(URL url) throws IOException {
  15. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  16. //设置所有的http连接是否自动处理重定向;
  17. // public static void HttpURLConnection.setFollowRedirects(boolean //followRedirects)
  18. //设置本次连接是否自动处理重定向。设置成true,系统自动处理重定向;设置成false则需要自己从http reply中分析新的url自己重新连接。
  19. connection.setInstanceFollowRedirects(HttpURLConnection.getFollowRedirects());
  20. return connection;
  21. }

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

  1. static void setConnectionParametersForRequest(HttpURLConnection connection,
  2. Request<?> request) throws IOException, AuthFailureError {
  3. switch (request.getMethod()) {
  4. case Method.DEPRECATED_GET_OR_POST:
  5. byte[] postBody = request.getPostBody();
  6. if (postBody != null) {
  7. connection.setDoOutput(true);
  8. connection.setRequestMethod("POST");
  9. connection.addRequestProperty(HEADER_CONTENT_TYPE,
  10. request.getPostBodyContentType());
  11. DataOutputStream out = new DataOutputStream(connection.getOutputStream());
  12. out.write(postBody);
  13. out.close();
  14. }
  15. break;
  16. case Method.GET:
  17. connection.setRequestMethod("GET");
  18. break;
  19. case Method.DELETE:
  20. connection.setRequestMethod("DELETE");
  21. break;
  22. case Method.POST:
  23. connection.setRequestMethod("POST");
  24. addBodyIfExists(connection, request);
  25. break;
  26. case Method.PUT:
  27. connection.setRequestMethod("PUT");
  28. addBodyIfExists(connection, request);
  29. break;
  30. case Method.HEAD:
  31. connection.setRequestMethod("HEAD");
  32. break;
  33. case Method.OPTIONS:
  34. connection.setRequestMethod("OPTIONS");
  35. break;
  36. case Method.TRACE:
  37. connection.setRequestMethod("TRACE");
  38. break;
  39. case Method.PATCH:
  40. connection.setRequestMethod("PATCH");
  41. addBodyIfExists(connection, request);
  42. break;
  43. default:
  44. throw new IllegalStateException("Unknown method type.");
  45. }
  46. }
  47. private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
  48. throws IOException, AuthFailureError {
  49. byte[] body = request.getBody();
  50. if (body != null) {
  51. connection.setDoOutput(true);
  52. connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
  53. DataOutputStream out = new DataOutputStream(connection.getOutputStream());
  54. out.write(body);
  55. out.close();
  56. }
  57. }

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

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

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

  1. int responseCode = connection.getResponseCode();
  2. if (responseCode == -1) {
  3. throw new IOException("Could not retrieve response code from HttpUrlConnection.");
  4. }
  5. StatusLine responseStatus = new BasicStatusLine(protocolVersion,
  6. connection.getResponseCode(), connection.getResponseMessage());
  7. BasicHttpResponse response = new BasicHttpResponse(responseStatus);
  8. if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
  9. response.setEntity(entityFromConnection(connection));
  10. }
  11. for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
  12. if (header.getKey() != null) {
  13. Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
  14. response.addHeader(h);
  15. }
  16. }
  17. private static boolean hasResponseBody(int requestMethod, int responseCode) {
  18. return requestMethod != Request.Method.HEAD
  19. && !(HttpStatus.SC_CONTINUE <= responseCode && responseCode < HttpStatus.SC_OK)
  20. && responseCode != HttpStatus.SC_NO_CONTENT
  21. && responseCode != HttpStatus.SC_NOT_MODIFIED;
  22. }
  23. private static HttpEntity entityFromConnection(HttpURLConnection connection) {
  24. BasicHttpEntity entity = new BasicHttpEntity();
  25. InputStream inputStream;
  26. try {
  27. inputStream = connection.getInputStream();
  28. } catch (IOException ioe) {
  29. inputStream = connection.getErrorStream();
  30. }
  31. entity.setContent(inputStream);
  32. entity.setContentLength(connection.getContentLength());
  33. entity.setContentEncoding(connection.getContentEncoding());
  34. entity.setContentType(connection.getContentType());
  35. return entity;
  36. }

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

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注