接上一章来讲网络请求的大体过程,Volley是如何封装并使用HttpURLConnection和HttpClient的。
目录
网络请求的基本逻辑
网络请求的交互层—>BasicNetwork
封装HttpURLConnection实现网络连接
RequestQueue.java
从
通过Volley我们能学到什么?(1) — 工作原理与设计模式的NetworkDispatcher.java中的调用网络请求开始讲起。。
- NetworkResponse networkResponse = mNetwork.performRequest(request);
Volley中网络请求一共分为三层,大体结构如下:
1、NetworkDispatcher获取Request对象并分发给BasicNetwork;
2、BasicNetwork得到Request对象后,配置头文件等配置信息分发给HttpStack;
3、HttpStack得到Request对象和配置信息后进行真正的网络请求;
4、HttpStack将网络请求的结果返回给BasicNetwork;
5、BasicNetwork得到返回结果,如果成功则返回给NetworkDispatcher,否则处理其他异常情况;
第一层NetworkDispatcher已讲完,这篇主要讲第二层(BasicNetwork)和第三层(HttpStack)。
网络请求的交互层
—>BasicNetwork
BasicNetwork.java
这一行代码直接调用的是BasicNetwork.java中的performRequest()方法
- public NetworkResponse performRequest(Request<?> request) throws VolleyError {
- long requestStart = SystemClock.elapsedRealtime();
- while (true) {
- HttpResponse httpResponse = null;
- byte[] responseContents = null;
- Map<String, String> responseHeaders = Collections.emptyMap();
- try {
- // Gather headers.
- Map<String, String> headers = new HashMap<String, String>();
- addCacheHeaders(headers, request.getCacheEntry());
- httpResponse = mHttpStack.performRequest(request, headers);
- StatusLine statusLine = httpResponse.getStatusLine();
- int statusCode = statusLine.getStatusCode();
- responseHeaders = convertHeaders(httpResponse.getAllHeaders());
- // Handle cache validation.
- if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
- Entry entry = request.getCacheEntry();
- if (entry == null) {
- return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
- responseHeaders, true,
- SystemClock.elapsedRealtime() - requestStart);
- }
- // A HTTP 304 response does not have all header fields. We
- // have to use the header fields from the cache entry plus
- // the new ones from the response.
- // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
- entry.responseHeaders.putAll(responseHeaders);
- return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
- entry.responseHeaders, true,
- SystemClock.elapsedRealtime() - requestStart);
- }
- // Some responses such as 204s do not have content. We must check.
- if (httpResponse.getEntity() != null) {
- responseContents = entityToBytes(httpResponse.getEntity());
- } else {
- // Add 0 byte response as a way of honestly representing a
- // no-content request.
- responseContents = new byte[0];
- }
- // if the request is slow, log it.
- long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
- logSlowRequests(requestLifetime, request, responseContents, statusLine);
- if (statusCode < 200 || statusCode > 299) {
- throw new IOException();
- }
- return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
- SystemClock.elapsedRealtime() - requestStart);
- } catch (SocketTimeoutException e) {
- attemptRetryOnException("socket", request, new TimeoutError());
- } catch (ConnectTimeoutException e) {
- attemptRetryOnException("connection", request, new TimeoutError());
- } catch (MalformedURLException e) {
- throw new RuntimeException("Bad URL " + request.getUrl(), e);
- } catch (IOException e) {
- int statusCode = 0;
- NetworkResponse networkResponse = null;
- if (httpResponse != null) {
- statusCode = httpResponse.getStatusLine().getStatusCode();
- } else {
- throw new NoConnectionError(e);
- }
- VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
- if (responseContents != null) {
- networkResponse = new NetworkResponse(statusCode, responseContents,
- responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
- if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
- statusCode == HttpStatus.SC_FORBIDDEN) {
- attemptRetryOnException("auth",
- request, new AuthFailureError(networkResponse));
- } else {
- // TODO: Only throw ServerError for 5xx status codes.
- throw new ServerError(networkResponse);
- }
- } else {
- throw new NetworkError(networkResponse);
- }
- }
- }
- }
1、首先创建循环机制,使请求失败可以再次请求(3行);
2、添加头部信息(9 - 10行);
3、调用HttpStack对象进行网络请求,进入网络请求的第三层(11行);
4、获取请求结果的状态(12 - 13行);
5、更新头部信息(14行);
6、如果获取的状态行是304则说明之前已获取过数据且服务器未做修改,直接返回上次请求的数据(16 - 32行);
7、将数据内容写入到responseContents里(35 - 41行);
8、请求完成,如果访问总时长超过3秒将访问的详细信息打印出来(44 - 45行);
9、如果状态码返回是异常,报IO异常,跳转到下面的catch中(47 - 49);
10、如果是连接异常则重新连接(53、55行);
11、如果超时,则跳出循环向上抛异常,超时时间最大值可设置(56行);
12、处理IO异常,如果是因为访问无权限或禁止访问这种认证问题,可以再给一次机会,否则直接抛异常(59 - 80行);
多次网络请求机制
BasicNetwork.java
接下来我们来看一下Volley多次访问逻辑,入口为attemptRetryOnException()方法。
- private static void attemptRetryOnException(String logPrefix, Request<?> request,
- VolleyError exception) throws VolleyError {
- RetryPolicy retryPolicy = request.getRetryPolicy();
- int oldTimeout = request.getTimeoutMs();
- try {
- retryPolicy.retry(exception);
- } catch (VolleyError e) {
- request.addMarker(
- String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
- throw e;
- }
- request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
- }
1、获取Request对象里面的RetryPolicy对象,并获取超时时间(3 - 4行);
2、调用RetryPolicy对象的retry()方法来检测是否需要重新访问(7行);
向下看retry()的源码
DefaultRetryPolicy.java
- public void retry(VolleyError error) throws VolleyError {
- mCurrentRetryCount++;
- mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
- if (!hasAttemptRemaining()) {
- throw error;
- }
- }
1、重新访问次数加一(2行);
2、累加超时时间(3行);
3、判断重新访问次数是否大于最大访问次数(默认是1,可自定义),大于最大值报异常跳出循环(4行);
封装HttpURLConnection实现网络连接(HttpClient暂不整理)
如果是Android2.3即以上版本调用上面HttpStack对象的performRequest()方法的会映射到HurlStack类里面的performRequest()方法来实现网络连接。
HurlStack.java
- public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
- throws IOException, AuthFailureError {
- 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 = 0;
- try {
- responseCode = connection.getResponseCode();
- } catch (IOException e) {
- connection.getErrorStream();
- 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;
1、得到URL并获取头部信息(3 - 6行);
2、mUrlRewriter是干什么的?mUrlRewriter默认为null,但如果赋值的话,所有使用到的网络请求URL统一替换成mUrlRewriter中的URL,应该是项目测试用的(7 - 13行);
3、创建HttpURLConnection对象,并做一些连接前的配置,看一下这个方法的细节(15行);
调用createConnection()方法创建一个HttpURLConnection对象,createConnection()方法里面只是判断了一下是不是通过https访问。如果是https则在
HttpsURLConnection这个类里面的两个静态对象初始化,
如果是http的话不做处理。因为如果是http的话后面的执行直接用connection这个引用就可以,如果是https的话由于已经在
HttpsURLConnection这个类中把必要的静态数据初始化了所以可以直接将connection强行转换(170行、179 - 181行);
4、设置头部(16 - 18行);
5、设置连接参数,支持8种请求方式(19行);
6、获取连接最终状态,如果是-1说明不能获取到,向上抛异常(22 - 34行);
7、配置BasicHttpResponse对象以便在最后返回它(35 - 37行);
8、将body写入到BasicHttpResponse对象中(38行);
9、将头部信息写入到BasicHttpResponse对象中(39 - 44行);
这样整体的网络结构就差不多了。如果继续深入可以研究上传功能的源码和HttpUrlConnect的源码。