Volley框架解析(五)-----HttpStack接口及其默认实现类解析

Volley框架解析(五)—–HttpStack接口及其默认实现类解析


1. 前言(可直接无视跳过= =

    历经前面的四篇,终于涉及到网络请求核心的内容了,前面都在做一些准备工作,以及对request队列调度以及维护工作。之前一直说,httpResponse = mHttpStack.performRequest(request, headers);这句话是网络请求的核心,因为这句话一出来,神不知鬼不觉的request就被发出去了,并且还带回来一个HttpResponse的实例= =,有没有感觉被忽悠了,今天就深入进去一探究竟0.0。

2. HttpStack.java

    接口类,里面包含了一个方法,performRequest()

/**
 * An HTTP stack abstraction.
 */
public interface HttpStack {
    /**
     * Performs an HTTP request with the given parameters.
     * 用传入给定的参数来模拟Http请求
     * 
     * 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().
     * 如果传入的request.getPostBody()为空,则发送一个Get类型的请求,否则发送一个Post类型请求
     * 
     * @param request the request to perform
     * 即将发送的初始请求,也是volley自己写的= =,进去看看
     * (还需要添加上额外的header
     * 
     * @param additionalHeaders additional headers to be sent together with
     *         {@link Request#getHeaders()}
     * 需要添加到该request上的header的信息
     *
     * @return the HTTP response
     *
     */
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
        throws IOException, AuthFailureError;

}

3. HttpClientStack.java

    当sdk版本小于2.3时,Volley会选择用HttpClient来实现请求的发送。

/**
 * An HttpStack that performs request over an {@link HttpClient}.
 * 在sdk小于2.3的时候
 * 选用HttpClient来实现网络请求
 */
public class HttpClientStack implements HttpStack {

    /**
     * 官方文档
     * Interface for an HTTP client. 
     * HTTP clients encapsulate a smorgasbord of objects required to execute HTTP requests while handling cookies, 
     * authentication, connection management, and other features. 
     * HTTP Clients将发送http请求需要需要做出的信息
     * Thread safety of HTTP clients depends on the implementation and configuration of the specific client. 
     * 
     */
    protected final HttpClient mClient;

    //Http请求头里面的固定格式
    private final static String HEADER_CONTENT_TYPE = "Content-Type";

    public HttpClientStack(HttpClient client) {
        mClient = client;
    }

    //在组合出一个请求的过程中,向请求体中添加Header的方法,Header是以键值对的形式存在的
    private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
        for (String key : headers.keySet()) {
            httpRequest.setHeader(key, headers.get(key));
        }
    }

    /**
     * NameValuePair 官方文档
     * A simple class encapsulating an attribute/value pair. 
     * 
     * 该函数将传入的Map里面存放的值进一步转化成由NameValuePair子类组成的数组中
     */
    @SuppressWarnings("unused")
    private static List<NameValuePair> getPostParameterPairs(Map<String, String> postParams) {
        List<NameValuePair> result = new ArrayList<NameValuePair>(postParams.size());
        for (String key : postParams.keySet()) {
            result.add(new BasicNameValuePair(key, postParams.get(key)));
        }
        return result;
    }


     //该函数也就是实现HttpStack接口需要实现的方法,用来执行Request的方法
    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {

        /**
         * 传入请求体和额外需要添加入的头部
         * 生成并返回一个HttpUriRequest
         */
        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);

        /**
         * 这个方法在前面实现了,将这些传入的键值对全部添加到httpRequest里面去
         */
        addHeaders(httpRequest, additionalHeaders);
        addHeaders(httpRequest, request.getHeaders());

        /**
         * 一个protected方法,留给子类可以实现的方法(本类中并没有什么东西),在这里会调用。
         */
        onPrepareRequest(httpRequest);

        /**
         * HttpParams 官方文档
         * Represents a collection of HTTP protocol and framework parameters. 
         * 说白了就是Http协议和框架的相关参数
         */
        HttpParams httpParams = httpRequest.getParams();
        int timeoutMs = request.getTimeoutMs();

        /**
         * HttpConnectionParams 官方文档
         * An adaptor for accessing connection parameters in HttpParams. 
         * 一个用来访问请求参数的适配器
         * Note that the implements relation to CoreConnectionPNames is for compatibility with existing application code only. 
         * References to the parameter names should use the interface, not this class. 
         */

        /* Sets the timeout until a connection is established.
         * 该方法用来设置时间限制,
         * A value of zero means the timeout is not used. The default value is zero. 
         * 如果timeout设置为0则表示该限时没有启用,默认为0
         */
        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);

        /**
         * Sets the default socket timeout (SO_TIMEOUT) in milliseconds which is the timeout for waiting for data. 
         * 设置请求发出后等待网络响应并返回数据的限时
         * A timeout value of zero is interpreted as an infinite timeout. 
         * 如果timeout值为0则意味着无限等待,没有等待限时,同时也是默认的值
         * This value is used when no socket timeout is set in the method parameters. 
         */
        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);

        /**
         * 执行了HttpClient类中的execute方法
         * 方法描述为 Executes a request using the default context.
         * 方法结束后将返回一个HttpResponse,也就是请求的结果类
         */ 
        return mClient.execute(httpRequest);
    }

    /**
     * Creates the appropriate subclass of HttpUriRequest for passed in request.
     * 根据传入的Request种类不同
     * 创建不同的HttpUriRequest子类(也就是下面的HttpGet等等)
     * 下面做的工作和HurlStack.java里面做的工作差不多
     * 设置header,以及是否需要传入请求携带的参数
     * 只是本类中用HttpClient实现,后者用的是HttpURLConnection实现的
     */
    @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.");
        }
    }

    private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
            Request<?> request) throws AuthFailureError {
        byte[] body = request.getBody();
        if (body != null) {
            HttpEntity entity = new ByteArrayEntity(body);
            httpRequest.setEntity(entity);
        }
    }

    /**
     * Called before the request is executed using the underlying HttpClient.
     *
     * <p>Overwrite in subclasses to augment the request.</p>
     */
    protected void onPrepareRequest(HttpUriRequest request) throws IOException {
        // Nothing.
    }

    /**
     * The HttpPatch class does not exist in the Android framework, so this has been defined here.
     * = =在HttpUriClient的子类中没有支持Patch的请求方法
     * 在这里volley实现了= =
     */
    public static final class HttpPatch extends HttpEntityEnclosingRequestBase {

        public final static String METHOD_NAME = "PATCH";

        public HttpPatch() {
            super();
        }

        public HttpPatch(final URI uri) {
            super();
            setURI(uri);
        }

        /**
         * @throws IllegalArgumentException if the uri is invalid.
         */
        public HttpPatch(final String uri) {
            super();
            setURI(URI.create(uri));
        }

        @Override
        public String getMethod() {
            return METHOD_NAME;
        }

    }
}

4.HurlStack.java

    在sdk大于2.3的android手机上,Volley选择用HttpURLConnection来实现网络请求。

/**
 * An {@link HttpStack} based on {@link HttpURLConnection}.
 */

/**
 * 当os version 版本在2.3以上,也就是sdk >= 9 的时候
 * 选用这个接口作为HttpStack, 用到了HttpURLConnection
 * 关于HttpURLConnection,官方解释为:
 * An URLConnection for HTTP (RFC 2616) used to send and receive data over the web.
 * Data may be of any type and length. 
 * This class may be used to send and receive streaming data whose length is not known in advance.
 * 用来发送和接受数据,数据可以为任意的形式及长度
 * 这个类常用来发送和接受数据流里面长度不定的数据.
 */
public class HurlStack implements HttpStack {

    /*
     * 请求header中的一个关键字
     * content-type代表着被发送的请求中主体内容
     * 可以设置application/json等格式
     */
    private static final String HEADER_CONTENT_TYPE = "Content-Type";

    /**
     * An interface for transforming URLs before use.
     * 一个用来在使用url之前,将url处理的接口工具
     * 可能是用来规范url格式的一个工具= =
     */ 
    public interface UrlRewriter {
        /**
         * Returns a URL to use instead of the provided one, or null to indicate
         * this URL should not be used at all.
         */
        public String rewriteUrl(String originalUrl);
    }

    private final UrlRewriter mUrlRewriter;

    /**
     * The abstract factory implementation to create SSLSockets.
     * 是一个抽象工厂类,用来创建SSLSockets(还是不懂是个什么鬼
     * 
     * 对于SSLSocket,官方的解释是这样的:
     * The extension of Socket providing secure protocols like SSL (Secure Sockets Layer) or TLS (Transport Layer Security).
     * 是Socket的子类,并在之基础上新增了类似于SSL或者TLS等等的安全协议.
     */
    private final SSLSocketFactory mSslSocketFactory;

    public HurlStack() {
        this(null);
    }

    /**
     * @param urlRewriter Rewriter to use for request URLs
     */
    public HurlStack(UrlRewriter urlRewriter) {
        this(urlRewriter, null);
    }

    /**
     * @param urlRewriter Rewriter to use for request URLs
     * @param sslSocketFactory SSL factory to use for HTTPS connections
     */
    public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
        mUrlRewriter = urlRewriter;
        mSslSocketFactory = sslSocketFactory;
    }


    /**
     * 该函数为HttpStack的接口
     */
    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        /**
         * 得到请求的url
         */
        String url = request.getUrl();

        /**
         * 创建一个新的HashMap
         * 用来存放请求的header的信息
         */
        HashMap<String, String> map = new HashMap<String, String>();

        /**
         * 将原request(volley自己封装的一个request类)中的header
         * 和另外需要添加入header的信息都整合起来
         */
        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字符串形式规范成一个URL的类对象
         */
        URL parsedUrl = new URL(url);

        /**
         * HurlStack类是在sdk>=2.3的android版本上使用的
         * 这里面用到了HttpURLConnection类
         * 在函数里面打开了并返回了一个HttpURLConnection
         * 设置了HttpURLConnection的响应超时阀值
         */
        HttpURLConnection connection = openConnection(parsedUrl, request);

        /**
         * 开始给HttpURLConnection添加header的信息
         * 用addRequestProperty()函数将header以键值对的形式填入
         */
        for (String headerName : map.keySet()) {
            connection.addRequestProperty(headerName, map.get(headerName));
        }

        /**
         * 根据request种类的不同
         * 分别用不同的方式来处理其中的参数
         */
        setConnectionParametersForRequest(connection, request);

        // Initialize HttpResponse with data from the HttpURLConnection.
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);

        int responseCode = connection.getResponseCode();
        if (responseCode == -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;
    }

    /**
     * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
     * @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;
    }

    /**
     * Create an {@link HttpURLConnection} for the specified {@code url}.
     */
    protected HttpURLConnection createConnection(URL url) throws IOException {

        return (HttpURLConnection) url.openConnection();
    }

    /**
     * Opens an {@link HttpURLConnection} with parameters.
     * 通过给的url和参数,打开一个HttpURLConnection
     * @param url
     * @return an open connection
     * @throws IOException
     */
    private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {

        HttpURLConnection connection = createConnection(url);

        /**
         * 通过Request.java中的函数
         * 获取到该request上所设置的服务器最大响应时间阀值
         * 该阀值默认是2500ms,而且可能会随着retry的次数而增大
         */
        int timeoutMs = request.getTimeoutMs();

        /**
         * 给connection设置上请求超时时间
         */
        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) {
            ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
        }

        return connection;
    }

    @SuppressWarnings("deprecation")
    /* package */
    /**
     * switch不同的请求方法
     * 来以不同的方式给HttpURLConnection添加请求参数
     */ 
    static void setConnectionParametersForRequest(HttpURLConnection connection,
            Request<?> request) throws IOException, AuthFailureError {
        switch (request.getMethod()) {

            /**
             * 在构造Request的时候如果没有指明请求方式
             * DEPRECATED_GET_OR_POST为其默认值
             * 通过postBody是否为Null来区别POST和GET
             * 这两种最常用的请求方式
             */
            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.
                /**
                 * 不要用这个参数了= =,因为不能处理什么DELETE之类的
                 * 该方法已经过时了。
                 */
                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设置请求的方式
                     */
                    connection.setRequestMethod("POST");

                    /**
                     * 设置http请求头中的content-type参数
                     */
                    connection.addRequestProperty(HEADER_CONTENT_TYPE,
                            request.getPostBodyContentType());
                    DataOutputStream out = new DataOutputStream(connection.getOutputStream());
                    out.write(0);
                    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.");
        }
    }

    /**
     * 如果存在请求参数的话
     * 获取到connection的输出流对象
     * 并创建一个DataOutputStream对象
     * 用于向服务器写入需要传递的参数
     */
    private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
            throws IOException, AuthFailureError {
        byte[] body = request.getBody();
        if (body != null) {
            connection.setDoOutput(true);
            connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
            DataOutputStream out = new DataOutputStream(connection.getOutputStream());
            out.write(body);
            out.close();
        }
    }
}

    如果上面的注释有什么错误的地方,还望大家给与指正= =。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值