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

来源:互联网 发布:linux nc 发送数据 编辑:程序博客网 时间:2024/06/16 07:09

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 0