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的源码:
HttpClientStack和HurlStack都是实现网络请求的,只是应用的场景稍有不同,在android版本2.3以下使用HttpClientStack,2.3以上使用HttpClientStack。在类Volley中可以看出:
另外说一点volley中的Request,从字面理解上是请求,但是其实不是一个真正的网络请求,个人理解为是对真正的网络请求的各个属性的描述。HttpStack是真正的网络请求,Request则是封装了超时时间、请求方式、参数、url等网络请求所必须的参数。通过变换不同的属性参数(最基本的如请求方式GET POST),可以获取不同的网络请求。
OK,现在在说下我对于HttpClientStack和HurlStack的理解,二者的工作思路是这样的:
1.首先从volley的Request内获取各个属性、如超时间、请求方式、参数和url
2.创建网络请求,HttpClientStack是创建HttpClient ,HurlStack是创建HttpURLConnection
3.对网络请求设置各个属性参数
4.定义执行网路请求的方法,并获取响应,将响应返回出去
上面说的是思路,现在贴下源码,是类的全部源码,本人不想只贴片段、容易断层:
HurlStack源码:
本人习惯于在源码中添加大量注释,这样看起来比干说要清晰明了很多,所以篇幅可能有些长。
HttpClientStack源码:
volley的真正的网络请求就是这些了,注意这里是定义网络请求和网络请求的方法,但是调用执行网络请求其实是在NetWork接口及其实现类BasicNetwork内。
在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内。
相关文章推荐
- iOS开发笔记4:HTTP网络通信及网络编程
- 社交网络中校花评比算法(转自百度百科)
- red5-server源码:https://github.com/Red5/red5-server
- Netty HTTP 服务端入门开发
- https
- iOS网络编程开发笔记1—JSON和XML数据解析
- 甘特图与网络图
- 【365酒店用品网】谈网络信息的重要性
- HttpPut,HttpPost,HttpDelete,HttpGet请求
- Golang实现简单tcp服务器01 -- 概述
- Golang实现简单tcp服务器02 -- 实现echo服务器/客户端
- Golang实现简单tcp服务器03 -- 文本广播式聊天服务器/客户端
- Golang实现简单tcp服务器04 -- 服务器的粘包处理
- tcp 聊天工具客户端
- Windows下QT4.8.4编译环境的搭建(转载http://blog.csdn.net/bestgonghuibin/article/details/38933141)
- 计算机网络学习(4)
- IOS菜鸟的所感所思(八)——coreData与网络歌曲的本地化
- 关于TCP/IP协议栈的三次握手,四次挥手
- 【网络流】最大流最简单的Ford-Fulkerson算法
- 配置httpd.conf支持PHP