Volley网络请求框架

本文详细解析了Volley网络请求的实现过程,包括其内部结构、网络请求的交互层及HttpURLConnection封装等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接上一章来讲网络请求的大体过程,Volley是如何封装并使用HttpURLConnection和HttpClient的。

目录
网络请求的基本逻辑
网络请求的交互层—>BasicNetwork
封装HttpURLConnection实现网络连接

RequestQueue.java
通过Volley我们能学到什么?(1) — 工作原理与设计模式的NetworkDispatcher.java中的调用网络请求开始讲起。。
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 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()方法 
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public NetworkResponse performRequest(Request<?> request) throws VolleyError {  
  2.     long requestStart = SystemClock.elapsedRealtime();  
  3.     while (true) {  
  4.         HttpResponse httpResponse = null;  
  5.         byte[] responseContents = null;  
  6.         Map<String, String> responseHeaders = Collections.emptyMap();  
  7.         try {  
  8.             // Gather headers.  
  9.             Map<String, String> headers = new HashMap<String, String>();  
  10.             addCacheHeaders(headers, request.getCacheEntry());  
  11.             httpResponse = mHttpStack.performRequest(request, headers);  
  12.             StatusLine statusLine = httpResponse.getStatusLine();  
  13.             int statusCode = statusLine.getStatusCode();  
  14.             responseHeaders = convertHeaders(httpResponse.getAllHeaders());  
  15.             // Handle cache validation.  
  16.             if (statusCode == HttpStatus.SC_NOT_MODIFIED) {  
  17.                 Entry entry = request.getCacheEntry();  
  18.                 if (entry == null) {  
  19.                     return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,  
  20.                             responseHeaders, true,  
  21.                             SystemClock.elapsedRealtime() - requestStart);  
  22.                 }  
  23.                 // A HTTP 304 response does not have all header fields. We  
  24.                 // have to use the header fields from the cache entry plus  
  25.                 // the new ones from the response.  
  26.                 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5  
  27.                 entry.responseHeaders.putAll(responseHeaders);  
  28.                 return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,  
  29.                         entry.responseHeaders, true,  
  30.                         SystemClock.elapsedRealtime() - requestStart);  
  31.             }  
  32.   
  33.             // Some responses such as 204s do not have content.  We must check.  
  34.             if (httpResponse.getEntity() != null) {  
  35.               responseContents = entityToBytes(httpResponse.getEntity());  
  36.             } else {  
  37.               // Add 0 byte response as a way of honestly representing a  
  38.               // no-content request.  
  39.               responseContents = new byte[0];  
  40.             }  
  41.   
  42.             // if the request is slow, log it.  
  43.             long requestLifetime = SystemClock.elapsedRealtime() - requestStart;  
  44.             logSlowRequests(requestLifetime, request, responseContents, statusLine);  
  45.   
  46.             if (statusCode < 200 || statusCode > 299) {  
  47.                 throw new IOException();  
  48.             }  
  49.             return new NetworkResponse(statusCode, responseContents, responseHeaders, false,  
  50.                     SystemClock.elapsedRealtime() - requestStart);  
  51.         } catch (SocketTimeoutException e) {  
  52.             attemptRetryOnException("socket", request, new TimeoutError());  
  53.         } catch (ConnectTimeoutException e) {  
  54.             attemptRetryOnException("connection", request, new TimeoutError());  
  55.         } catch (MalformedURLException e) {  
  56.             throw new RuntimeException("Bad URL " + request.getUrl(), e);  
  57.         } catch (IOException e) {  
  58.             int statusCode = 0;  
  59.             NetworkResponse networkResponse = null;  
  60.             if (httpResponse != null) {  
  61.                 statusCode = httpResponse.getStatusLine().getStatusCode();  
  62.             } else {  
  63.                 throw new NoConnectionError(e);  
  64.             }  
  65.             VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());  
  66.             if (responseContents != null) {  
  67.                 networkResponse = new NetworkResponse(statusCode, responseContents,  
  68.                         responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);  
  69.                 if (statusCode == HttpStatus.SC_UNAUTHORIZED ||  
  70.                         statusCode == HttpStatus.SC_FORBIDDEN) {  
  71.                     attemptRetryOnException("auth",  
  72.                         request, new AuthFailureError(networkResponse));  
  73.                 } else {  
  74.                     // TODO: Only throw ServerError for 5xx status codes.  
  75.                     throw new ServerError(networkResponse);  
  76.                 }  
  77.             } else {  
  78.                 throw new NetworkError(networkResponse);  
  79.             }  
  80.         }  
  81.     }  
  82. }  

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()方法。 
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private static void attemptRetryOnException(String logPrefix, Request<?> request,  
  2.             VolleyError exception) throws VolleyError {  
  3.         RetryPolicy retryPolicy = request.getRetryPolicy();  
  4.         int oldTimeout = request.getTimeoutMs();  
  5.   
  6.         try {  
  7.             retryPolicy.retry(exception);  
  8.         } catch (VolleyError e) {  
  9.             request.addMarker(  
  10.                     String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));  
  11.             throw e;  
  12.         }  
  13.         request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));  
  14.     }  

1、获取Request对象里面的RetryPolicy对象,并获取超时时间(3 - 4行);
2、调用RetryPolicy对象的retry()方法来检测是否需要重新访问(7行);

向下看retry()的源码

DefaultRetryPolicy.java
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void retry(VolleyError error) throws VolleyError {  
  2.     mCurrentRetryCount++;  
  3.     mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);  
  4.     if (!hasAttemptRemaining()) {  
  5.         throw error;  
  6.     }  
  7. }  

1、重新访问次数加一(2行);
2、累加超时时间(3行);
3、判断重新访问次数是否大于最大访问次数(默认是1,可自定义),大于最大值报异常跳出循环(4行);

封装HttpURLConnection实现网络连接(HttpClient暂不整理)

如果是Android2.3即以上版本调用上面HttpStack对象的performRequest()方法的会映射到HurlStack类里面的performRequest()方法来实现网络连接。

HurlStack.java
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)  
  2.             throws IOException, AuthFailureError {  
  3.         String url = request.getUrl();  
  4.         HashMap<String, String> map = new HashMap<String, String>();  
  5.         map.putAll(request.getHeaders());  
  6.         map.putAll(additionalHeaders);  
  7.         if (mUrlRewriter != null) {  
  8.             String rewritten = mUrlRewriter.rewriteUrl(url);  
  9.             if (rewritten == null) {  
  10.                 throw new IOException("URL blocked by rewriter: " + url);  
  11.             }  
  12.             url = rewritten;  
  13.         }  
  14.         URL parsedUrl = new URL(url);  
  15.         HttpURLConnection connection = openConnection(parsedUrl, request);  
  16.         for (String headerName : map.keySet()) {  
  17.             connection.addRequestProperty(headerName, map.get(headerName));  
  18.         }  
  19.         setConnectionParametersForRequest(connection, request);  
  20.         // Initialize HttpResponse with data from the HttpURLConnection.  
  21.         ProtocolVersion protocolVersion = new ProtocolVersion("HTTP"11);  
  22.         int responseCode = 0;  
  23.         try {  
  24.             responseCode = connection.getResponseCode();  
  25.         } catch (IOException e) {  
  26.             connection.getErrorStream();  
  27.             responseCode = connection.getResponseCode();  
  28.         }  
  29.   
  30.         if (responseCode == -1) {  
  31.             // -1 is returned by getResponseCode() if the response code could not be retrieved.  
  32.             // Signal to the caller that something was wrong with the connection.  
  33.             throw new IOException("Could not retrieve response code from HttpUrlConnection.");  
  34.         }  
  35.         StatusLine responseStatus = new BasicStatusLine(protocolVersion,  
  36.                 connection.getResponseCode(), connection.getResponseMessage());  
  37.         BasicHttpResponse response = new BasicHttpResponse(responseStatus);  
  38.         response.setEntity(entityFromConnection(connection));  
  39.         for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {  
  40.             if (header.getKey() != null) {  
  41.                 Header h = new BasicHeader(header.getKey(), header.getValue().get(0));  
  42.                 response.addHeader(h);  
  43.             }  
  44.         }  
  45.         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的源码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值