您的位置:首页 > 理论基础 > 计算机网络

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

2015-06-02 08:44 621 查看
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内。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: