Android中的volley_3_网络请求HttpStack、HttpClientStack和HurlStack


demo下载:http://download.csdn.net/detail/vvzhouruifeng/8747599


在volley中真正的网络请求是由HttpStack定义的,这是一个接口,它唯一的一个方法就是执行网路请求获取响应。而HttpClientStack和HurlStack是该接口的实现类,对应不同的网络请求的底层实现,HttpClientStack是基于HttpClient的,HurlStack是基于HttpURLConnection的。

先看HttpStack的源码:

package com.android.volley.toolbox;

import com.android.volley.AuthFailureError;
import com.android.volley.Request;

import org.apache.http.HttpResponse;

import java.io.IOException;
import java.util.Map;

/**
 * An HTTP stack abstraction. 处理 Http 请求,返回请求结果。目前 Volley 中有基于 HttpURLConnection
 * 的HurlStack和 基于 Apache HttpClient 的HttpClientStack。
 */
public interface HttpStack {
	/**
	 * Performs an HTTP request with the given parameters.
	 * 
	 * <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
	 *            所要执行的请求,是一个抽象类,在具体执行时,需要传入该类的子类,比如volley已封装的StringRequest
	 *            JonsRequest等
	 *            ,除此之外,还可以自己实现该接口,定制自己的请求(比如官网的GsonRequest就是这么做的),扩展性极强
	 * @param additionalHeaders
	 *            additional headers to be sent together with
	 *            {@link Request#getHeaders()}第二个参数表示发起请求之前,添加额外的请求 Headers
	 * @return the HTTP response
	 *         请求的响应,这是原生的,在BasicNetwork内执行该方法后,会对HttpResponse进行解析处理
	 *         ,封装为volley内的响应
	 */
	public HttpResponse performRequest(Request<?> request,
			Map<String, String> additionalHeaders) throws IOException,
			AuthFailureError;

}

HttpClientStack和HurlStack都是实现网络请求的,只是应用的场景稍有不同,在android版本2.3以下使用HttpClientStack,2.3以上使用HttpClientStack。在类Volley中可以看出:

              // 默认 Android2.3 及以上基于 HttpURLConnection,2.3 以下基于 HttpClient 实现
		if (stack == null) {
			if (Build.VERSION.SDK_INT >= 9) {
				//基于HttpURLConnection
				stack = new HurlStack();
			} else {
				// Prior to Gingerbread, HttpUrlConnection was unreliable.
				// See:
				// http://android-developers.blogspot.com/2011/09/androids-http-clients.html
				//基于HttpClient实现,类 AndroidHttpClient继承了HttpClient
				stack = new HttpClientStack(
						AndroidHttpClient.newInstance(userAgent));
			}
		}

另外说一点volley中的Request,从字面理解上是请求,但是其实不是一个真正的网络请求,个人理解为是对真正的网络请求的各个属性的描述。HttpStack是真正的网络请求,Request则是封装了超时时间、请求方式、参数、url等网络请求所必须的参数。通过变换不同的属性参数(最基本的如请求方式GET  POST),可以获取不同的网络请求。

OK,现在在说下我对于HttpClientStack和HurlStack的理解,二者的工作思路是这样的:

1.首先从volley的Request内获取各个属性、如超时间、请求方式、参数和url

2.创建网络请求,HttpClientStack是创建HttpClient   ,HurlStack是创建HttpURLConnection

3.对网络请求设置各个属性参数

4.定义执行网路请求的方法,并获取响应,将响应返回出去


上面说的是思路,现在贴下源码,是类的全部源码,本人不想只贴片段、容易断层:

HurlStack源码

import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.Request.Method;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;

/**
 * An {@link HttpStack} based on {@link HttpURLConnection}.
 * 
 * 1.该类基于HttpURLConnection的网络请求,该类实现了接口HttpStack,而HttpStack中定义了唯一的一个方法
 * public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
 * 该方法的作用就是发出网络请求获取响应。
 * 2.该类的具体实现,是这样的,从请求中获取超时时间和请求方式(post get)设置链接,然后访问服务器从服务器拿数据。
 * 从服务器返回的数据(connection.getInputStream())中获取响应头信息和响应体信息,依据这些信息创建一个响应 并返回
 * 3.主要是看performRequest这个方法,暂不考虑UrlRewriter和https
 */
public class HurlStack implements HttpStack {

    private static final String HEADER_CONTENT_TYPE = "Content-Type";

    /**
     * An interface for transforming URLs before use.
     */
    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;
    private final SSLSocketFactory mSslSocketFactory;

    /**
     * volley中默认用的是该构造方法
     */
    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;
    }

    /**
     * 实现接口的方法
     * 参数:Request<?> request  代表所要执行的请求,Request是一个接口,其实现类有StringRequest JsonRequest  ImageRequest等,代表具体的请求
     * 参数:Map<String, String> additionalHeaders   代表附加的一些头信息
     * 
     * 返回值:HttpResponse   请求网络后的响应
     */
    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
    	//每个request内都封装了目标url  获取url
        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);
        //获取状态码
        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();
    }

    /**
     * 创建一个链接,基于HttpURLConnection
     * Opens an {@link HttpURLConnection} with parameters.
     * @param url 请求的目标
     * @param request  要执行的请求 主要是为了获取该请求中设置的超时时间
     * @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);

        //获取url的协议 如果是https 就执行https协议
        // 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 */ 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:
            	//设置链接为post请求
                connection.setRequestMethod("POST");
                addBodyIfExists(connection, request);
                break;
            case Method.PUT:
                connection.setRequestMethod("PUT");
                addBodyIfExists(connection, request);
                break;
            default:
                throw new IllegalStateException("Unknown method type.");
        }
    }

    //当请求方式是POST的时候添加  其实就是post请求的参数  该参数是在volley的Request内获取的
    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();
        }
    }
}

本人习惯于在源码中添加大量注释,这样看起来比干说要清晰明了很多,所以篇幅可能有些长。

HttpClientStack源码:

import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.Request.Method;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * An HttpStack that performs request over an {@link HttpClient}.
 * 该类是HttpStack另一个重要的实现类,该类是基于HttpClient链接的,当低于2.3版本时,volley默认是用HttpClient来实现网络请求的
 * 该类的主要工作:
 * 1.从Request内获取请求方式、超时时间、目标url和参数
 * 2.创建基于HttpClient的网络请求,并设置上述参数
 * 3.执行网络请求,获取到网络响应并返回
 * 
 * 需要注意:
 * 1.HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);  这是面向接口的编程
 * 2.volley中的Request其实不是一个请求,应该理解为对网络请求的描述,内部定义了一个网络请求所必须的各个属性参数,比如请求方式、目标url、参数、超时时间等,
 * 真正的网络请求是由HttpClientStack和HurlStack来实现的,而这两个类中的网络请求的各个属性参数来自于volley的Request
 */
public class HttpClientStack implements HttpStack {
	//创建一个链接
    protected final HttpClient mClient;

    private final static String HEADER_CONTENT_TYPE = "Content-Type";

    //通过构造方法传入
    public HttpClientStack(HttpClient client) {
        mClient = client;
    }

    /**
     * 向请求中添加一些请求头信息
     * @Description
     * @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));
        }
    }

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

    //实现接口的方法,用于执行具体的请求,返回值为一个响应,BasicNetWork通过执行该方法获得响应,并对响应进行处理获得NetworkResponse
    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
    	//创建一个请求,主要是获得Request内的请求方式和参数,返回不同的请求
    	//HttpUriRequest是HttpPost  HttpGet的接口
    	//这里依然是面向接口的编程,不局限于某一个请求(get  post put等),而是一个接口
        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);
    }

    /**
     * Creates the appropriate subclass of HttpUriRequest for passed in request.
     * 获取请求中定义的请求方式,和请求中的url  从而创建相应的链接
     */
    @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:
            	//返回一个GET请求,请求的url是从volley的请求中获取的
                return new HttpGet(request.getUrl());
            case Method.DELETE:
                return new HttpDelete(request.getUrl());
            case Method.POST: {
            	//创建post请求
                HttpPost postRequest = new HttpPost(request.getUrl());
                //添加请求头信息
                postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                //添加该请求的参数,就是post的参数
                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;
            }
            default:
                throw new IllegalStateException("Unknown request method.");
        }
    }

    //从Request内取得数据体,如果数据体不为空就设置到请求实体中。其实这里是post请求的参数
    private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
            Request<?> request) throws AuthFailureError {
    	//从request中获取参数体
        byte[] body = request.getBody();
        if (body != null) {
        	//设置POST网络请求的参数
            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.
    }
}

volley的真正的网络请求就是这些了,注意这里是定义网络请求和网络请求的方法,但是调用执行网络请求其实是在NetWork接口及其实现类BasicNetwork内。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值