文章转自: http://blog.csdn.net/crazy__chen/article/details/46610461
Volley中网络加载有两种方式,分别是HurlStack与HttpClientStack,我们来看Volley.Java中的一段代码
- if (stack == null) {
- if (Build.VERSION.SDK_INT >= 9) {
- stack = new HurlStack();
- } else {
-
-
- stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
- }
- }
由此可见,如果没有设置stack,则根据当前adk版本自动选择。在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。
而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。对于新的应用程序应该更加偏向于使用HttpURLConnection,因为在以后的工作当中我们也会将更多的时间放在优化HttpURLConnection上面。
为此,我们需要分别来看这两个类,在看这两个之前,我们先来看它们一个简单的父类HttpStack
-
-
-
-
- public interface HttpStack {
-
-
-
-
-
-
-
-
-
-
-
- public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
- throws IOException, AuthFailureError;
-
- }
该父类主要规定了,子类必须有一个根据request请求数据,并且返回HttpResponse类的方法
,那么接下来我们先看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);
-
- ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
- 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);
- 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()方法
-
-
-
-
-
-
-
- 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);
-
-
- if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
- ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
- }
-
- return connection;
- }
-
-
-
-
- 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")
-
-
-
-
-
-
-
-
- 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.");
- }
- }
最后获取响应,将响应头信息包装成StatusLine对象,再包装成BasicHttpResponse对象
- StatusLine responseStatus = new BasicStatusLine(protocolVersion,
- connection.getResponseCode(), connection.getResponseMessage());
- BasicHttpResponse response = new BasicHttpResponse(responseStatus);
然后为BasicHttpResponse加入响应内容
- response.setEntity(entityFromConnection(connection));
entityFromConnection(HttpURLConnection connection)方法:
-
-
-
-
-
-
- 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();
-
-
- HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
- HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
- return mClient.execute(httpRequest);
- }
请求步骤,首先是根据请求方式,构造HttpUriRequest对象,并且设置请求参数
- HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
createHttpRequest()方法:
-
-
-
-
- @SuppressWarnings("deprecation")
-
- static HttpUriRequest createHttpRequest(Request<?> request,
- Map<String, String> additionalHeaders) throws AuthFailureError {
- switch (request.getMethod()) {
- case Method.DEPRECATED_GET_OR_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方法:
-
-
-
-
-
- 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的封装,然而这个封装并不是很完美,所以造成了版本之间的差异。值得一提的是,为什么volley适合频繁的网络请求,不适合文件上传等大数据请求呢?那是因为每次添加request会做缓存 如果文件太大会照成oom,如果要解决这个问题,看源码其实我们还有一个办法,就是添加request之前把requests设置request.setShouldCache(false),对于用法来说 是很不方便的事情。