标签:
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)); } }
而在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; }
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; }
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);
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 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之争
标签:
原文地址:http://blog.csdn.net/crazy__chen/article/details/46610461