zoukankan      html  css  js  c++  java
  • volley源代码解析(六)--HurlStack与HttpClientStack之争

    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));
                }
            }

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

    而在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;
    
    }
    

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


    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;
        }

    整个方法分成几个步骤,首先是将请求參数。存储到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()方法

    /**
         * 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);

    然后为BasicHttpResponse增加响应内容

     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对象,而且设置请求參数

    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>是怎么设计的。



  • 相关阅读:
    [置顶] 国外程序员推荐:每个程序员都应读的书
    Android入门(2) 基本控件介绍、4种布局
    Struts2标签之<s:select>
    百度地图API学习总结
    Environment
    SD卡中文件夹和文件的操作
    Webpack v4.8.3 快速入门指南
    javascript代码模块化解决方案
    MVC,MVP 和 MVVM 的图示 阮一峰
    webpack Uncaught ReferenceError: Swiper is not defined
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/6935019.html
Copyright © 2011-2022 走看看