volley介绍07

-------------------------------------------------------------------------------

转载:http://blog.csdn.net/crazy__chen/article/details/46610461

-------------------------------------------------------------------------------

Volley中网络加载有两种方式,分别是HurlStack与HttpClientStack,我们来看Volley.java中的一段代码

[java] view plain copy
 
  1. if (stack == null) {//如果没有限定stack  
  2.             if (Build.VERSION.SDK_INT >= 9) {//adk版本在9或者以上  
  3.                 stack = new HurlStack();  
  4.             } else {  
  5.                 // Prior to Gingerbread, HttpUrlConnection was unreliable.  
  6.                 // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html  
  7.                 stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));  
  8.             }  
  9.         }  


由此可见,如果没有设置stack,则根据当前adk版本自动选择。在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。

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

为此,我们需要分别来看这两个类,在看这两个之前,我们先来看它们一个简单的父类HttpStack

[java] view plain copy
 
  1. /** 
  2.  * An HTTP stack abstraction. 
  3.  * 抽象的http栈 
  4.  */  
  5. public interface HttpStack {  
  6.     /** 
  7.      * Performs an HTTP request with the given parameters. 
  8.      * 根据参数,执行http请求 
  9.      * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise, 
  10.      * and the Content-Type header is set to request.getPostBodyContentType().</p> 
  11.      * 
  12.      * @param request the request to perform 
  13.      * @param additionalHeaders additional headers to be sent together with 
  14.      *         {@link Request#getHeaders()} 
  15.      * @return the HTTP response 
  16.      */  
  17.     public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)  
  18.         throws IOException, AuthFailureError;  
  19.   
  20. }  


该父类主要规定了,子类必须有一个根据request请求数据,并且返回HttpResponse类的方法

OK,接下来我们先看HurlStack,这个类使用的是HttpURLConnection作为连接方式,在adk较高版本推荐使用(其实目前市场上2.3的系统已经很少见了)

我们直接看这个类的核心方法performRequest()

[java] view plain copy
 
  1. @Override  
  2.     public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)  
  3.             throws IOException, AuthFailureError {  
  4.         String url = request.getUrl();  
  5.         HashMap<String, String> map = new HashMap<String, String>();  
  6.         map.putAll(request.getHeaders());  
  7.         map.putAll(additionalHeaders);  
  8.         if (mUrlRewriter != null) {  
  9.             String rewritten = mUrlRewriter.rewriteUrl(url);  
  10.             if (rewritten == null) {  
  11.                 throw new IOException("URL blocked by rewriter: " + url);  
  12.             }  
  13.             url = rewritten;  
  14.         }  
  15.         URL parsedUrl = new URL(url);  
  16.         HttpURLConnection connection = openConnection(parsedUrl, request);//开启连接  
  17.         for (String headerName : map.keySet()) {//添加请求参数  
  18.             connection.addRequestProperty(headerName, map.get(headerName));  
  19.         }  
  20.         setConnectionParametersForRequest(connection, request);//设置请求方式  
  21.         // Initialize HttpResponse with data from the HttpURLConnection.  
  22.         ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);//http协议  
  23.         int responseCode = connection.getResponseCode();//获取响应状态  
  24.         if (responseCode == -1) {//-1说明没有响应,抛出异常  
  25.             // -1 is returned by getResponseCode() if the response code could not be retrieved.  
  26.             // Signal to the caller that something was wrong with the connection.  
  27.             throw new IOException("Could not retrieve response code from HttpUrlConnection.");  
  28.         }  
  29.         StatusLine responseStatus = new BasicStatusLine(protocolVersion,  
  30.                 connection.getResponseCode(), connection.getResponseMessage());//响应状态类  
  31.         BasicHttpResponse response = new BasicHttpResponse(responseStatus);  
  32.         response.setEntity(entityFromConnection(connection));//解析响应实体  
  33.         for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {//添加响应头  
  34.             if (header.getKey() != null) {  
  35.                 Header h = new BasicHeader(header.getKey(), header.getValue().get(0));  
  36.                 response.addHeader(h);  
  37.             }  
  38.         }  
  39.         return response;  
  40.     }  


整个方法分成几个步骤,首先是将请求参数,存储到map当中

[java] view plain copy
 
  1. HashMap<String, String> map = new HashMap<String, String>();  
  2.        map.putAll(request.getHeaders());  
  3.        map.putAll(additionalHeaders);  

然后是开启url连接

[java] view plain copy
 
  1. URL parsedUrl = new URL(url);  
  2.         HttpURLConnection connection = openConnection(parsedUrl, request);//开启连接  

来看openConnection()方法

[java] view plain copy
 
  1. /** 
  2.      * Opens an {@link HttpURLConnection} with parameters. 
  3.      * 开启网络连接 
  4.      * @param url 
  5.      * @return an open connection 
  6.      * @throws IOException 
  7.      */  
  8.     private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {  
  9.         HttpURLConnection connection = createConnection(url);  
  10.   
  11.         int timeoutMs = request.getTimeoutMs();  
  12.         connection.setConnectTimeout(timeoutMs);  
  13.         connection.setReadTimeout(timeoutMs);  
  14.         connection.setUseCaches(false);  
  15.         connection.setDoInput(true);  
  16.   
  17.         // use caller-provided custom SslSocketFactory, if any, for HTTPS  
  18.         if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {//https  
  19.             ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);  
  20.         }  
  21.   
  22.         return connection;  
  23.     }  
  24.   
  25.    /** 
  26.      * Create an {@link HttpURLConnection} for the specified {@code url}. 
  27.      */  
  28.     protected HttpURLConnection createConnection(URL url) throws IOException {  
  29.         return (HttpURLConnection) url.openConnection();  
  30.     }  

这个方法主要就是调用url.openConnevtion()从而返回一个HttpURLConnection对象,其中的一些超时设置,是由request本身提供的

另外还根据url是否带有https,为HttpURLConnection设置setSSLSocketFactory(mSslSocketFactory对象是在构造方法中传入的)

得到HttpURLConnection,就设置请求参数

[java] view plain copy
 
  1. for (String headerName : map.keySet()) {//添加请求参数  
  2.             connection.addRequestProperty(headerName, map.get(headerName));  
  3.         }  

然后是确定请求方式(GET,POST还是别的)

[java] view plain copy
 
  1. setConnectionParametersForRequest(connection, request);//设置请求方式  

setConnectionParametersForRequest方法:

[java] view plain copy
 
  1. @SuppressWarnings("deprecation")  
  2.     /** 
  3.      * 设置请求方式 
  4.      * @param connection 
  5.      * @param request 
  6.      * @throws IOException 
  7.      * @throws AuthFailureError 
  8.      */  
  9.     /* package */   
  10.     static void setConnectionParametersForRequest(HttpURLConnection connection,  
  11.             Request<?> request) throws IOException, AuthFailureError {  
  12.         switch (request.getMethod()) {  
  13.             case Method.DEPRECATED_GET_OR_POST:  
  14.                 // This is the deprecated way that needs to be handled for backwards compatibility.  
  15.                 // If the request's post body is null, then the assumption is that the request is  
  16.                 // GET.  Otherwise, it is assumed that the request is a POST.  
  17.                 byte[] postBody = request.getPostBody();  
  18.                 if (postBody != null) {  
  19.                     // Prepare output. There is no need to set Content-Length explicitly,  
  20.                     // since this is handled by HttpURLConnection using the size of the prepared  
  21.                     // output stream.  
  22.                     connection.setDoOutput(true);  
  23.                     connection.setRequestMethod("POST");  
  24.                     connection.addRequestProperty(HEADER_CONTENT_TYPE,  
  25.                             request.getPostBodyContentType());  
  26.                     DataOutputStream out = new DataOutputStream(connection.getOutputStream());  
  27.                     out.write(postBody);  
  28.                     out.close();  
  29.                 }  
  30.                 break;  
  31.             case Method.GET:  
  32.                 // Not necessary to set the request method because connection defaults to GET but  
  33.                 // being explicit here.  
  34.                 connection.setRequestMethod("GET");  
  35.                 break;  
  36.             case Method.DELETE:  
  37.                 connection.setRequestMethod("DELETE");  
  38.                 break;  
  39.             case Method.POST:  
  40.                 connection.setRequestMethod("POST");  
  41.                 addBodyIfExists(connection, request);  
  42.                 break;  
  43.             case Method.PUT:  
  44.                 connection.setRequestMethod("PUT");  
  45.                 addBodyIfExists(connection, request);  
  46.                 break;  
  47.             case Method.HEAD:  
  48.                 connection.setRequestMethod("HEAD");  
  49.                 break;  
  50.             case Method.OPTIONS:  
  51.                 connection.setRequestMethod("OPTIONS");  
  52.                 break;  
  53.             case Method.TRACE:  
  54.                 connection.setRequestMethod("TRACE");  
  55.                 break;  
  56.             case Method.PATCH:  
  57.                 connection.setRequestMethod("PATCH");  
  58.                 addBodyIfExists(connection, request);  
  59.                 break;  
  60.             default:  
  61.                 throw new IllegalStateException("Unknown method type.");  
  62.         }  
  63.     }  

最后获取响应,将响应头信息包装成StatusLine对象,再包装成BasicHttpResponse对象

[java] view plain copy
 
  1. StatusLine responseStatus = new BasicStatusLine(protocolVersion,  
  2.                 connection.getResponseCode(), connection.getResponseMessage());//响应状态类  
  3.         BasicHttpResponse response = new BasicHttpResponse(responseStatus);  


然后为BasicHttpResponse加入响应内容

[java] view plain copy
 
  1. response.setEntity(entityFromConnection(connection));//解析响应实体  

entityFromConnection(HttpURLConnection connection)方法:

[java] view plain copy
 
  1. /** 
  2.      * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}. 
  3.      * <br>解析出响应实体 
  4.      * @param connection 
  5.      * @return an HttpEntity populated with data from <code>connection</code>. 
  6.      */  
  7.     private static HttpEntity entityFromConnection(HttpURLConnection connection) {  
  8.         BasicHttpEntity entity = new BasicHttpEntity();  
  9.         InputStream inputStream;  
  10.         try {  
  11.             inputStream = connection.getInputStream();  
  12.         } catch (IOException ioe) {  
  13.             inputStream = connection.getErrorStream();  
  14.         }  
  15.         entity.setContent(inputStream);  
  16.         entity.setContentLength(connection.getContentLength());  
  17.         entity.setContentEncoding(connection.getContentEncoding());  
  18.         entity.setContentType(connection.getContentType());  
  19.         return entity;  
  20.     }  

最后,加入响应头部内容

[java] view plain copy
 
  1. for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {//添加响应头  
  2.             if (header.getKey() != null) {  
  3.                 Header h = new BasicHeader(header.getKey(), header.getValue().get(0));  
  4.                 response.addHeader(h);  
  5.             }  
  6.         }  

OK,这样就返回了一个具有完整信息的HttpResponse对象。整个过程比较简单,是常规的网络请求内容。

接下来我们看HttpClientStack的实现

同样,直接来看performRequest()方法

[java] view plain copy
 
  1. @Override  
  2.     public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)  
  3.             throws IOException, AuthFailureError {  
  4.         HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);  
  5.         addHeaders(httpRequest, additionalHeaders);//添加缓存头  
  6.         addHeaders(httpRequest, request.getHeaders());//添加请求头  
  7.         onPrepareRequest(httpRequest);//请求预处理  
  8.         HttpParams httpParams = httpRequest.getParams();  
  9.         int timeoutMs = request.getTimeoutMs();  
  10.         // TODO: Reevaluate this connection timeout based on more wide-scale  
  11.         // data collection and possibly different for wifi vs. 3G.  
  12.         HttpConnectionParams.setConnectionTimeout(httpParams, 5000);  
  13.         HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);  
  14.         return mClient.execute(httpRequest);  
  15.     }  


请求步骤,首先是根据请求方式,构造HttpUriRequest对象,并且设置请求参数

[java] view plain copy
 
  1. HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);  

createHttpRequest()方法:

[java] view plain copy
 
  1. /** 
  2.      * Creates the appropriate subclass of HttpUriRequest for passed in request. 
  3.      * 根据请求方式返回对应HttpUriRequest的子类 
  4.      */  
  5.     @SuppressWarnings("deprecation")  
  6.     /* protected */   
  7.     static HttpUriRequest createHttpRequest(Request<?> request,  
  8.             Map<String, String> additionalHeaders) throws AuthFailureError {  
  9.         switch (request.getMethod()) {  
  10.             case Method.DEPRECATED_GET_OR_POST: {  
  11.                 // This is the deprecated way that needs to be handled for backwards compatibility.  
  12.                 // If the request's post body is null, then the assumption is that the request is  
  13.                 // GET.  Otherwise, it is assumed that the request is a POST.  
  14.                 byte[] postBody = request.getPostBody();  
  15.                 if (postBody != null) {  
  16.                     HttpPost postRequest = new HttpPost(request.getUrl());  
  17.                     postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());  
  18.                     HttpEntity entity;  
  19.                     entity = new ByteArrayEntity(postBody);  
  20.                     postRequest.setEntity(entity);  
  21.                     return postRequest;  
  22.                 } else {  
  23.                     return new HttpGet(request.getUrl());  
  24.                 }  
  25.             }  
  26.             case Method.GET:  
  27.                 return new HttpGet(request.getUrl());  
  28.             case Method.DELETE:  
  29.                 return new HttpDelete(request.getUrl());  
  30.             case Method.POST: {  
  31.                 HttpPost postRequest = new HttpPost(request.getUrl());  
  32.                 postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());  
  33.                 setEntityIfNonEmptyBody(postRequest, request);//设置请求参数  
  34.                 return postRequest;  
  35.             }  
  36.             case Method.PUT: {  
  37.                 HttpPut putRequest = new HttpPut(request.getUrl());  
  38.                 putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());  
  39.                 setEntityIfNonEmptyBody(putRequest, request);  
  40.                 return putRequest;  
  41.             }  
  42.             case Method.HEAD:  
  43.                 return new HttpHead(request.getUrl());  
  44.             case Method.OPTIONS:  
  45.                 return new HttpOptions(request.getUrl());  
  46.             case Method.TRACE:  
  47.                 return new HttpTrace(request.getUrl());  
  48.             case Method.PATCH: {  
  49.                 HttpPatch patchRequest = new HttpPatch(request.getUrl());  
  50.                 patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());  
  51.                 setEntityIfNonEmptyBody(patchRequest, request);  
  52.                 return patchRequest;  
  53.             }  
  54.             default:  
  55.                 throw new IllegalStateException("Unknown request method.");  
  56.         }  
  57.     }  

从createHttpRequest()方法可以看出,在HttpClient中,只要根据请求方式,new一个HttpGet/HttpPost/....对象就可以了(而urlstack这一步是真的connnection而言的)

接着是为HttpUriRequest对象设置请求头部

[java] view plain copy
 
  1. addHeaders(httpRequest, additionalHeaders);//添加缓存头  
  2.        addHeaders(httpRequest, request.getHeaders());//添加请求头  

addHeaders方法:

[java] view plain copy
 
  1. /** 
  2.      * 添加响应头 
  3.      * @param httpRequest 
  4.      * @param headers 
  5.      */  
  6.     private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {  
  7.         for (String key : headers.keySet()) {  
  8.             httpRequest.setHeader(key, headers.get(key));  
  9.         }  
  10.     }  

最后,将HttpUriRequest对象交给httpClient执行

[java] view plain copy
 
  1. return mClient.execute(httpRequest);  

OK,HttpClientStack比我们想象的还要简单,起码比HurlStack简单,这是当然的,因为使用httpClient方式,其本质就是对urlConnection的封装,然而这个封装并不是很完美,所以造成了版本之间的差异。

到此为止,给大家介绍了HurlStack与HttpClientStack这两个类,同时也说明了真正的网络请求在哪里执行。

下一篇文章,将会来了解Response<T>的使用,Response<T>是Volley整个过程中,辗转获得的最终目的,作为响应实体,我们来看一下Response<T>是怎么设计的。

原文地址:https://www.cnblogs.com/aprz512/p/5316739.html