揭秘 Android Volley 网络模块
一、引言
在移动应用开发中,网络通信是不可或缺的一部分。无论是获取远程数据、提交表单还是与服务器进行实时交互,都需要高效可靠的网络模块支持。Android Volley 作为 Google 官方推出的轻量级网络通信框架,凭借其简单易用的 API、高效的请求调度和强大的缓存机制,成为了 Android 开发者首选的网络解决方案之一。
本文将深入剖析 Android Volley 框架的网络模块,从源码级别详细解析其实现原理和工作流程。我们将探讨网络模块的架构设计、核心类和接口的实现、HTTP 请求的处理流程、网络响应的解析与分发,以及错误处理和重试机制等关键技术点。通过本文的学习,读者将对 Volley 网络模块有一个全面而深入的理解,能够在实际开发中更加灵活和高效地使用 Volley 进行网络通信。
二、网络模块概述
2.1 网络模块在 Volley 框架中的位置
Volley 框架整体架构可以分为五个主要部分:请求队列(RequestQueue)、缓存模块(Cache)、网络模块(Network)、响应分发器(ResponseDelivery)和请求(Request)。网络模块作为其中的核心组件之一,主要负责处理所有从缓存模块无法满足的网络请求,与远程服务器进行通信,并将响应数据返回给请求队列。
2.2 网络模块的主要功能
Volley 网络模块的主要功能包括:
- HTTP 请求的发送:将应用程序的请求转换为 HTTP 请求,并发送到远程服务器。
- 网络响应的接收:接收服务器返回的 HTTP 响应,并进行初步处理。
- 响应数据的解析:将服务器返回的数据解析为应用程序可以使用的格式。
- 错误处理和重试:处理网络请求过程中出现的各种错误,并根据配置进行重试。
- 请求优先级处理:根据请求的优先级对请求进行排序和处理。
2.3 网络模块的核心组件
Volley 网络模块的核心组件包括:
- Network 接口:定义了网络请求的基本操作,是网络模块的核心接口。
- BaseNetwork 类:Network 接口的抽象实现,提供了一些基础功能。
- BasicNetwork 类:BaseNetwork 的具体实现,使用 HttpStack 执行实际的 HTTP 请求。
- HttpStack 接口:定义了执行 HTTP 请求的基本操作。
- HurlStack 类:HttpStack 的实现,使用 Android 内置的 HttpURLConnection 执行 HTTP 请求。
- HttpClientStack 类:HttpStack 的实现,使用 Apache HttpClient 执行 HTTP 请求(Android 2.3 及以下版本使用)。
- NetworkResponse 类:封装了从网络获取的原始响应数据。
- NetworkDispatcher 类:网络调度器,负责从网络请求队列中取出请求并执行。
三、Network 接口分析
3.1 Network 接口定义
Network 接口是 Volley 网络模块的核心接口,定义了执行网络请求的基本方法。所有网络实现类都必须实现这个接口。
下面是 Network 接口的源码:
/**
* 网络接口,定义了执行网络请求的基本方法
*/
public interface Network {
/**
* 执行网络请求
*
* @param request 需要执行的请求
* @return 网络响应
* @throws VolleyError 如果请求过程中发生错误
*/
public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
3.2 Network 接口的作用
Network 接口是 Volley 网络模块的抽象层,它将网络请求的具体实现与上层逻辑分离。通过这种设计,Volley 可以支持多种不同的 HTTP 客户端实现(如 HttpURLConnection 和 Apache HttpClient),并允许开发者自定义网络实现以满足特定需求。
四、BaseNetwork 类分析
4.1 BaseNetwork 类概述
BaseNetwork 是 Network 接口的抽象实现,提供了一些基础功能,如请求头处理、响应缓存处理、重试策略等。所有具体的网络实现类都应该继承这个类。
4.2 BaseNetwork 类的成员变量
下面是 BaseNetwork 类的主要成员变量及其作用:
/**
* 网络请求的基础实现类,提供了一些通用的网络请求处理功能
*/
public abstract class BaseNetwork implements Network {
/** 日志标签 */
protected static final String TAG = "Volley";
/** 每次重试的退避乘数 */
protected static final float DEFAULT_BACKOFF_MULT = 1f;
/** 默认的套接字超时时间(毫秒) */
protected static final int DEFAULT_SOCKET_TIMEOUT_MS = 2500;
/** 默认的套接字缓冲区大小(字节) */
protected static final int DEFAULT_SOCKET_BUFFER_SIZE = 8192;
/** 响应数据的字符集 */
protected static final String DEFAULT_RESPONSE_CONTENT_CHARSET = "ISO-8859-1";
/** 调试模式开关 */
protected final boolean mIsDebug;
/** 用于执行 HTTP 请求的 HttpStack */
protected final HttpStack mHttpStack;
/** 用于执行 HTTPS 请求的 SSLSocketFactory */
private final SSLSocketFactory mSslSocketFactory;
// 其他成员变量...
}
4.3 BaseNetwork 类的构造函数
BaseNetwork 类的构造函数接收一个 HttpStack 对象作为参数,用于执行实际的 HTTP 请求:
/**
* 创建一个新的 BaseNetwork 实例
*
* @param httpStack 用于执行 HTTP 请求的 HttpStack
*/
protected BaseNetwork(HttpStack httpStack) {
// 调用重载的构造函数,使用默认的 SSLSocketFactory
this(httpStack, null);
}
/**
* 创建一个新的 BaseNetwork 实例,使用指定的 SSLSocketFactory
*
* @param httpStack 用于执行 HTTP 请求的 HttpStack
* @param sslSocketFactory 用于执行 HTTPS 请求的 SSLSocketFactory
*/
protected BaseNetwork(HttpStack httpStack, SSLSocketFactory sslSocketFactory) {
// 初始化 HttpStack
mHttpStack = httpStack;
// 如果未提供 SSLSocketFactory,则使用默认的
mSslSocketFactory = (sslSocketFactory != null) ? sslSocketFactory :
newSslSocketFactory();
// 检查是否处于调试模式
mIsDebug = VolleyLog.DEBUG;
}
4.4 BaseNetwork 类的核心方法
BaseNetwork 类实现了 Network 接口的 performRequest() 方法,并提供了一些辅助方法:
/**
* 执行网络请求的核心方法
*
* @param request 需要执行的请求
* @return 网络响应
* @throws VolleyError 如果请求过程中发生错误
*/
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
// 记录请求开始时间
long requestStart = SystemClock.elapsedRealtime();
// 循环处理请求,支持重试机制
while (true) {
// 用于存储 HTTP 响应的对象
HttpResponse httpResponse = null;
// 响应数据
byte[] responseContents = null;
// 响应头
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// 准备请求头,包括添加默认请求头和处理缓存相关的请求头
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
// 使用 HttpStack 执行 HTTP 请求,获取 HTTP 响应
httpResponse = mHttpStack.performRequest(request, headers);
// 获取 HTTP 状态码
int statusCode = httpResponse.getStatusCode();
// 获取响应头
responseHeaders = httpResponse.getHeaders();
// 如果状态码为 304(Not Modified),表示缓存有效
if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
// 从缓存条目中获取数据
Cache.Entry entry = request.getCacheEntry();
if (entry == null) {
// 如果缓存条目为空,返回一个空的响应
return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// 返回缓存数据
return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED,
entry.data, responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// 处理正常响应,读取响应数据
if (httpResponse.getContent() != null) {
responseContents = entityToBytes(httpResponse.getContent());
} else {
// 对于没有响应体的情况,创建一个空的字节数组
responseContents = new byte[0];
}
// 计算请求执行时间
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
// 记录请求执行时间和状态码
logSlowRequests(requestLifetime, request, responseContents, statusCode);
// 如果状态码不在 200-299 范围内,表示请求失败
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 (MalformedURLException e) {
// 处理 URL 格式错误,这通常是开发者的错误,不进行重试
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
// 处理其他 IO 异常
int statusCode = 0;
NetworkResponse networkResponse = null;
// 如果有 HTTP 响应,尝试获取状态码
if (httpResponse != null) {
statusCode = httpResponse.getStatusCode();
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
} else {
// 如果没有 HTTP 响应,记录错误信息
VolleyLog.e("IOException for %s", request.getUrl());
}
// 处理不同的状态码
if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED ||
statusCode == HttpURLConnection.HTTP_FORBIDDEN) {
// 处理认证失败的情况
attemptRetryOnException("auth", request,
new AuthFailureError(responseHeaders));
} else if (statusCode == 0) {
// 状态码为 0 表示连接问题,进行重试
attemptRetryOnException("network", request, new NetworkError(networkResponse));
} else {
// 其他状态码,返回服务器错误
attemptRetryOnException("server", request,
new ServerError(networkResponse));
}
}
}
}
/**
* 向请求头中添加缓存相关的头信息
*
* @param headers 要添加头信息的请求头映射
* @param entry 缓存条目,如果存在的话
*/
private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
// 如果没有缓存条目,不添加任何缓存头
if (entry == null) {
return;
}
// 添加 If-None-Match 头,用于验证缓存
if (entry.etag != null) {
headers.put("If-None-Match", entry.etag);
}
// 添加 If-Modified-Since 头,用于验证缓存
if (entry.serverDate > 0) {
headers.put("If-Modified-Since",
DateUtils.formatDate(new Date(entry.serverDate)));
}
}
/**
* 处理异常并重试请求
*
* @param logPrefix 日志前缀
* @param request 需要重试的请求
* @param exception 导致重试的异常
* @throws VolleyError 如果达到最大重试次数或不应该重试
*/
protected 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));
}
五、BasicNetwork 类分析
5.1 BasicNetwork 类概述
BasicNetwork 是 BaseNetwork 的具体实现,它使用 HttpStack 接口执行实际的 HTTP 请求。这个类提供了完整的网络请求处理功能,包括请求头处理、响应解析、错误处理和重试机制等。
5.2 BasicNetwork 类的构造函数
BasicNetwork 类的构造函数接收一个 HttpStack 对象作为参数:
/**
* 创建一个新的 BasicNetwork 实例
*
* @param httpStack 用于执行 HTTP 请求的 HttpStack
*/
public BasicNetwork(HttpStack httpStack) {
// 调用父类构造函数
super(httpStack);
}
/**
* 创建一个新的 BasicNetwork 实例,使用指定的 SSLSocketFactory
*
* @param httpStack 用于执行 HTTP 请求的 HttpStack
* @param sslSocketFactory 用于执行 HTTPS 请求的 SSLSocketFactory
*/
public BasicNetwork(HttpStack httpStack, SSLSocketFactory sslSocketFactory) {
// 调用父类构造函数
super(httpStack, sslSocketFactory);
}
5.3 BasicNetwork 类的核心方法
BasicNetwork 类主要重写了父类的一些方法,提供了具体的实现:
/**
* 执行网络请求的核心方法
*
* @param request 需要执行的请求
* @return 网络响应
* @throws VolleyError 如果请求过程中发生错误
*/
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
// 记录请求开始时间
long requestStart = SystemClock.elapsedRealtime();
// 循环处理请求,支持重试机制
while (true) {
// 用于存储 HTTP 响应的对象
HttpResponse httpResponse = null;
// 响应数据
byte[] responseContents = null;
// 响应头
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// 准备请求头,包括添加默认请求头和处理缓存相关的请求头
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
// 使用 HttpStack 执行 HTTP 请求,获取 HTTP 响应
httpResponse = mHttpStack.performRequest(request, headers);
// 获取 HTTP 状态码
int statusCode = httpResponse.getStatusCode();
// 获取响应头
responseHeaders = httpResponse.getHeaders();
// 如果状态码为 304(Not Modified),表示缓存有效
if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
// 从缓存条目中获取数据
Cache.Entry entry = request.getCacheEntry();
if (entry == null) {
// 如果缓存条目为空,返回一个空的响应
return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// 返回缓存数据
return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED,
entry.data, responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// 处理正常响应,读取响应数据
if (httpResponse.getContent() != null) {
responseContents = entityToBytes(httpResponse.getContent());
} else {
// 对于没有响应体的情况,创建一个空的字节数组
responseContents = new byte[0];
}
// 计算请求执行时间
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
// 记录请求执行时间和状态码
logSlowRequests(requestLifetime, request, responseContents, statusCode);
// 如果状态码不在 200-299 范围内,表示请求失败
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 (MalformedURLException e) {
// 处理 URL 格式错误,这通常是开发者的错误,不进行重试
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
// 处理其他 IO 异常
int statusCode = 0;
NetworkResponse networkResponse = null;
// 如果有 HTTP 响应,尝试获取状态码
if (httpResponse != null) {
statusCode = httpResponse.getStatusCode();
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
} else {
// 如果没有 HTTP 响应,记录错误信息
VolleyLog.e("IOException for %s", request.getUrl());
}
// 处理不同的状态码
if (statusCode == HttpURLConnection.HTTP_UNAUTHORIZED ||
statusCode == HttpURLConnection.HTTP_FORBIDDEN) {
// 处理认证失败的情况
attemptRetryOnException("auth", request,
new AuthFailureError(responseHeaders));
} else if (statusCode == 0) {
// 状态码为 0 表示连接问题,进行重试
attemptRetryOnException("network", request, new NetworkError(networkResponse));
} else {
// 其他状态码,返回服务器错误
attemptRetryOnException("server", request,
new ServerError(networkResponse));
}
}
}
}
/**
* 将 HTTP 实体转换为字节数组
*
* @param entity HTTP 实体
* @return 字节数组
* @throws IOException 如果读取实体时发生错误
*/
private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
// 创建一个 ByteArrayOutputStream 用于存储实体数据
PoolingByteArrayOutputStream bytes =
new PoolingByteArrayOutputStream(ByteArrayPool.get(),
(int) entity.getContentLength());
byte[] buffer = null;
try {
// 获取实体的输入流
InputStream in = entity.getContent();
if (in == null) {
// 如果输入流为空,抛出服务器错误
throw new ServerError();
}
// 获取实体的 Content-Type
String contentType = entity.getContentType() != null ?
entity.getContentType().getValue() : null;
// 如果 Content-Type 存在且是 gzip 格式,使用 GZIPInputStream 解压
if (contentType != null && contentType.contains("gzip")) {
in = new GZIPInputStream(in);
}
// 创建缓冲区
buffer = ByteArrayPool.get().getBuf(1024);
// 从输入流读取数据到缓冲区,并写入 ByteArrayOutputStream
int count;
while ((count = in.read(buffer)) != -1) {
bytes.write(buffer, 0, count);
}
// 将 ByteArrayOutputStream 中的数据转换为字节数组
return bytes.toByteArray();
} finally {
try {
// 关闭实体
entity.consumeContent();
} catch (IOException e) {
// 实体消费内容时出错,通常可以忽略
VolleyLog.v("Error occured when calling consumingContent");
}
// 释放缓冲区
ByteArrayPool.get().returnBuf(buffer);
// 关闭 ByteArrayOutputStream
bytes.close();
}
}
六、HttpStack 接口分析
6.1 HttpStack 接口定义
HttpStack 接口定义了执行 HTTP 请求的基本方法,是 Volley 网络模块的底层实现接口。
下面是 HttpStack 接口的源码:
/**
* HTTP 请求执行接口,定义了执行 HTTP 请求的基本方法
*/
public interface HttpStack {
/**
* 执行 HTTP 请求
*
* @param request 需要执行的请求
* @param additionalHeaders 额外的请求头
* @return HTTP 响应
* @throws IOException 如果请求过程中发生 IO 错误
*/
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
/**
* 关闭连接池
*/
public void shutdown();
}
6.2 HttpStack 接口的实现类
Volley 框架提供了两个主要的 HttpStack 实现类:
- HurlStack:使用 Android 内置的 HttpURLConnection 执行 HTTP 请求,是 Android 2.3 及以上版本的默认实现。
- HttpClientStack:使用 Apache HttpClient 执行 HTTP 请求,主要用于 Android 2.2 及以下版本。
七、HurlStack 类分析
7.1 HurlStack 类概述
HurlStack 是 HttpStack 接口的实现类,使用 Android 内置的 HttpURLConnection 执行 HTTP 请求。它是 Android 2.3 及以上版本的默认实现,因为 HttpURLConnection 在新的 Android 版本中性能更好,并且提供了更简洁的 API。
7.2 HurlStack 类的成员变量
下面是 HurlStack 类的主要成员变量及其作用:
/**
* 使用 HttpURLConnection 实现的 HttpStack
*/
public class HurlStack implements HttpStack {
/** 用于解析 URL 的正则表达式模式 */
private static final String HEADER_CONTENT_TYPE = "Content-Type";
/** 用于创建 HttpURLConnection 的工厂接口 */
private final UrlRewriter mUrlRewriter;
/** 用于 HTTPS 请求的 SSLSocketFactory */
private final SSLSocketFactory mSslSocketFactory;
// 其他成员变量...
}
7.3 HurlStack 类的构造函数
HurlStack 类提供了多个构造函数,允许开发者自定义 URL 重写和 SSLSocketFactory:
/**
* 创建一个新的 HurlStack 实例
*/
public HurlStack() {
this(null);
}
/**
* 创建一个新的 HurlStack 实例,使用指定的 URL 重写器
*
* @param urlRewriter URL 重写器,如果为 null 则不进行 URL 重写
*/
public HurlStack(UrlRewriter urlRewriter) {
this(urlRewriter, null);
}
/**
* 创建一个新的 HurlStack 实例,使用指定的 URL 重写器和 SSLSocketFactory
*
* @param urlRewriter URL 重写器,如果为 null 则不进行 URL 重写
* @param sslSocketFactory SSLSocketFactory,如果为 null 则使用默认的
*/
public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}
7.4 HurlStack 类的核心方法
HurlStack 类的核心方法是 performRequest(),用于执行 HTTP 请求:
/**
* 执行 HTTP 请求的核心方法
*
* @param request 需要执行的请求
* @param additionalHeaders 额外的请求头
* @return HTTP 响应
* @throws IOException 如果请求过程中发生 IO 错误
* @throws AuthFailureError 如果认证失败
*/
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
// 获取请求的 URL
String url = request.getUrl();
// 如果有 URL 重写器,使用它重写 URL
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
// 创建 HttpURLConnection
URL parsedUrl = new URL(url);
HttpURLConnection connection = createConnection(parsedUrl);
// 设置请求头
for (String headerName : request.getHeaders().keySet()) {
connection.addRequestProperty(headerName, request.getHeaders().get(headerName));
}
// 添加额外的请求头
for (Map.Entry<String, String> header : additionalHeaders.entrySet()) {
connection.addRequestProperty(header.getKey(), header.getValue());
}
// 设置请求方法
setConnectionParametersForRequest(connection, request);
// 获取响应码
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 表示连接异常关闭
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
// 处理重定向
if (responseCode == HttpURLConnection.HTTP_MOVED_PERM ||
responseCode == HttpURLConnection.HTTP_MOVED_TEMP ||
responseCode == HttpURLConnection.HTTP_SEE_OTHER) {
// 获取重定向的 URL
String newUrl = connection.getHeaderField("Location");
// 清除所有请求头
connection.disconnect();
// 创建新的连接并重试请求
connection = createConnection(new URL(newUrl));
// 重新设置请求头
for (String headerName : request.getHeaders().keySet()) {
connection.addRequestProperty(headerName, request.getHeaders().get(headerName));
}
// 重新添加额外的请求头
for (Map.Entry<String, String> header : additionalHeaders.entrySet()) {
connection.addRequestProperty(header.getKey(), header.getValue());
}
// 重新设置请求方法
setConnectionParametersForRequest(connection, request);
// 再次获取响应码
responseCode = connection.getResponseCode();
}
// 获取响应头
Map<String, String> responseHeaders = convertHeaders(connection.getHeaderFields());
// 如果响应码是 204(No Content),返回空的响应体
if (responseCode == HttpURLConnection.HTTP_NO_CONTENT) {
return new HttpResponse(responseCode, responseHeaders);
}
// 获取响应输入流
InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException e) {
// 如果获取输入流失败,尝试获取错误流
inputStream = connection.getErrorStream();
}
// 返回 HTTP 响应
return new HttpResponse(responseCode, responseHeaders,
connection.getContentLength(), inputStream);
}
/**
* 创建 HttpURLConnection 对象
*
* @param url URL 对象
* @return HttpURLConnection 对象
* @throws IOException 如果创建连接时发生错误
*/
protected HttpURLConnection createConnection(URL url) throws IOException {
// 打开 URL 连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置连接超时时间和读取超时时间
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
// 返回连接对象
return connection;
}
/**
* 根据请求类型设置 HttpURLConnection 的参数
*
* @param connection HttpURLConnection 对象
* @param request 请求对象
* @throws IOException 如果设置参数时发生错误
* @throws AuthFailureError 如果认证失败
*/
@SuppressWarnings("deprecation")
/* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
// 根据请求方法设置连接
switch (request.getMethod()) {
case Request.Method.DEPRECATED_GET_OR_POST:
// 处理旧的 GET 或 POST 方法
byte[] postBody = request.getPostBody();
if (postBody != null) {
// 如果有 POST 体,设置为 POST 方法
connection.setRequestMethod("POST");
addBodyIfExists(connection, request, postBody);
}
break;
case Request.Method.GET:
// 设置为 GET 方法
connection.setRequestMethod("GET");
break;
case Request.Method.DELETE:
// 设置为 DELETE 方法
connection.setRequestMethod("DELETE");
break;
case Request.Method.POST:
// 设置为 POST 方法
connection.setRequestMethod("POST");
addBodyIfExists(connection, request, request.getBody());
break;
case Request.Method.PUT:
// 设置为 PUT 方法
connection.setRequestMethod("PUT");
addBodyIfExists(connection, request, request.getBody());
break;
case Request.Method.HEAD:
// 设置为 HEAD 方法
connection.setRequestMethod("HEAD");
break;
case Request.Method.OPTIONS:
// 设置为 OPTIONS 方法
connection.setRequestMethod("OPTIONS");
break;
case Request.Method.TRACE:
// 设置为 TRACE 方法
connection.setRequestMethod("TRACE");
break;
case Request.Method.PATCH:
// 设置为 PATCH 方法
connection.setRequestMethod("PATCH");
addBodyIfExists(connection, request, request.getBody());
break;
default:
// 未知方法,抛出异常
throw new IllegalStateException("Unknown method type.");
}
}
/**
* 如果存在请求体,将其添加到 HttpURLConnection 中
*
* @param connection HttpURLConnection 对象
* @param request 请求对象
* @param body 请求体字节数组
* @throws IOException 如果添加请求体时发生错误
*/
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request,
byte[] body) throws IOException {
// 如果有请求体,设置连接为可输出
if (body != null) {
connection.setDoOutput(true);
// 设置 Content-Type 请求头
if (!connection.getRequestProperties().containsKey(HEADER_CONTENT_TYPE)) {
connection.setRequestProperty(HEADER_CONTENT_TYPE,
request.getBodyContentType());
}
// 获取输出流并写入请求体
OutputStream out = null;
try {
out = new BufferedOutputStream(connection.getOutputStream());
out.write(body);
} finally {
// 关闭输出流
if (out != null) {
out.close();
}
}
}
}
八、HttpClientStack 类分析
8.1 HttpClientStack 类概述
HttpClientStack 是 HttpStack 接口的另一个实现类,使用 Apache HttpClient 执行 HTTP 请求。它主要用于 Android 2.2 及以下版本,因为在这些版本中,HttpURLConnection 存在一些问题,而 Apache HttpClient 提供了更稳定的性能。
8.2 HttpClientStack 类的成员变量
下面是 HttpClientStack 类的主要成员变量及其作用:
/**
* 使用 Apache HttpClient 实现的 HttpStack
*/
public class HttpClientStack implements HttpStack {
/** 用于执行 HTTP 请求的 HttpClient */
protected final HttpClient mClient;
/** 用于存储请求和 URI 的映射 */
private final Map<Request<?>, URI> mRequestUriMap = new HashMap<Request<?>, URI>();
// 其他成员变量...
}
8.3 HttpClientStack 类的构造函数
HttpClientStack 类的构造函数接收一个 HttpClient 对象作为参数:
/**
* 创建一个新的 HttpClientStack 实例
*
* @param client 用于执行 HTTP 请求的 HttpClient
*/
public HttpClientStack(HttpClient client) {
mClient = client;
}
8.4 HttpClientStack 类的核心方法
HttpClientStack 类的核心方法是 performRequest(),用于执行 HTTP 请求:
/**
* 执行 HTTP 请求的核心方法
*
* @param request 需要执行的请求
* @param additionalHeaders 额外的请求头
* @return HTTP 响应
* @throws IOException 如果请求过程中发生 IO 错误
* @throws AuthFailureError 如果认证失败
*/
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
// 创建 HTTP 请求
HttpRequestBase httpRequest = createHttpRequest(request);
// 设置请求头
addHeaders(httpRequest, request.getHeaders());
addHeaders(httpRequest, additionalHeaders);
// 设置请求参数
setConnectionParametersForRequest(httpRequest, request);
// 存储请求和 URI 的映射
mRequestUriMap.put(request, httpRequest.getURI());
// 获取 HttpClient 实例
HttpClient client = getHttpClient();
// 执行 HTTP 请求
HttpResponse httpResponse;
try {
httpResponse = client.execute(httpRequest);
} catch (ClientProtocolException e) {
// 客户端协议异常
throw new IOException(e.getMessage());
} catch (IOException e) {
// IO 异常
throw e;
}
// 获取响应状态码
int statusCode = httpResponse.getStatusLine().getStatusCode();
// 获取响应头
Header[] headers = httpResponse.getAllHeaders();
Map<String, String> responseHeaders = new HashMap<String, String>();
for (Header header : headers) {
responseHeaders.put(header.getName(), header.getValue());
}
// 如果响应状态码为 301 或 302,处理重定向
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
// 获取重定向的 URL
String newUrl = responseHeaders.get("Location");
// 清除请求和 URI 的映射
mRequestUriMap.remove(request);
// 创建新的请求并执行
request.setUrl(newUrl);
return performRequest(request, additionalHeaders);
}
// 获取响应实体
HttpEntity entity = httpResponse.getEntity();
// 返回 HTTP 响应
return new HttpResponse(statusCode, responseHeaders,
entity == null ? -1 : entity.getContentLength(),
entity == null ? null : entity.getContent());
}
/**
* 根据请求类型创建相应的 HttpRequestBase 对象
*
* @param request 请求对象
* @return HttpRequestBase 对象
* @throws AuthFailureError 如果认证失败
*/
@SuppressWarnings("deprecation")
/* package */ static HttpRequestBase createHttpRequest(Request<?> request)
throws AuthFailureError {
// 根据请求方法创建相应的 HTTP 请求
switch (request.getMethod()) {
case Request.Method.DEPRECATED_GET_OR_POST:
// 处理旧的 GET 或 POST 方法
byte[] postBody = request.getPostBody();
if (postBody != null) {
// 如果有 POST 体,创建 HttpPost 请求
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader("Content-Type", request.getPostBodyContentType());
HttpEntity entity;
entity = new ByteArrayEntity(postBody);
postRequest.setEntity(entity);
return postRequest;
} else {
// 如果没有 POST 体,创建 HttpGet 请求
return new HttpGet(request.getUrl());
}
case Request.Method.GET:
// 创建 HttpGet 请求
return new HttpGet(request.getUrl());
case Request.Method.DELETE:
// 创建 HttpDelete 请求
return new HttpDelete(request.getUrl());
case Request.Method.POST:
// 创建 HttpPost 请求
HttpPost post = new HttpPost(request.getUrl());
post.addHeader("Content-Type", request.getBodyContentType());
setEntityIfNonEmptyBody(post, request);
return post;
case Request.Method.PUT:
// 创建 HttpPut 请求
HttpPut put = new HttpPut(request.getUrl());
put.addHeader("Content-Type", request.getBodyContentType());
setEntityIfNonEmptyBody(put, request);
return put;
case Request.Method.HEAD:
// 创建 HttpHead 请求
return new HttpHead(request.getUrl());
case Request.Method.OPTIONS:
// 创建 HttpOptions 请求
return new HttpOptions(request.getUrl());
case Request.Method.TRACE:
// 创建 HttpTrace 请求
return new HttpTrace(request.getUrl());
case Request.Method.PATCH:
// 创建 HttpPatch 请求
HttpPatch patch = new HttpPatch(request.getUrl());
patch.addHeader("Content-Type", request.getBodyContentType());
setEntityIfNonEmptyBody(patch, request);
return patch;
default:
// 未知方法,抛出异常
throw new IllegalStateException("Unknown method type.");
}
}
/**
* 为 HTTP 请求添加请求头
*
* @param httpRequest HTTP 请求
* @param headers 请求头映射
*/
private static void addHeaders(HttpRequestBase httpRequest, Map<String, String> headers) {
// 遍历请求头映射,为 HTTP 请求添加请求头
for (String headerName : headers.keySet()) {
httpRequest.addHeader(headerName, headers.get(headerName));
}
}
/**
* 根据请求类型设置 HTTP 请求的参数
*
* @param httpRequest HTTP 请求
* @param request 请求对象
* @throws IOException 如果设置参数时发生错误
* @throws AuthFailureError 如果认证失败
*/
@SuppressWarnings("deprecation")
/* package */ static void setConnectionParametersForRequest(
HttpRequestBase httpRequest, Request<?> request)
throws IOException, AuthFailureError {
// 设置超时时间
HttpParams httpParams = httpRequest.getParams();
httpParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, request.getTimeoutMs());
httpParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, request.getTimeoutMs());
// 如果是 HttpUriRequest 类型,设置重试处理
if (httpRequest instanceof HttpUriRequest) {
HttpUriRequest httpUriRequest = (HttpUriRequest) httpRequest;
// 设置 HTTP 方法
httpUriRequest.setURI(URI.create(request.getUrl()));
// 如果请求有重试策略,设置 HTTP 请求的重试处理
RetryPolicy retryPolicy = request.getRetryPolicy();
if (retryPolicy instanceof DefaultRetryPolicy) {
DefaultRetryPolicy defaultRetryPolicy = (DefaultRetryPolicy) retryPolicy;
httpParams.setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
httpParams.setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpRequestRetryHandler(defaultRetryPolicy.getCurrentRetryCount(), false));
}
}
}
/**
* 如果请求有非空的请求体,为 HTTP 请求设置实体
*
* @param httpRequestBase HTTP 请求
* @param request 请求对象
* @throws AuthFailureError 如果认证失败
*/
private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequestBase,
Request<?> request) throws AuthFailureError {
// 获取请求体
byte[] body = request.getBody();
if (body != null) {
// 如果有请求体,创建 ByteArrayEntity 并设置到 HTTP 请求中
HttpEntity entity = new ByteArrayEntity(body);
httpRequestBase.setEntity(entity);
}
}
九、NetworkResponse 类分析
9.1 NetworkResponse 类概述
NetworkResponse 类用于封装从网络获取的原始响应数据,包括状态码、响应数据、响应头以及其他相关信息。
9.2 NetworkResponse 类的成员变量
下面是 NetworkResponse 类的主要成员变量及其作用:
/**
* 网络响应类,封装了从网络获取的原始响应数据
*/
public class NetworkResponse {
/** HTTP 状态码 */
public final int statusCode;
/** 响应数据的字节数组 */
public final byte[] data;
/** 响应头 */
public final Map<String, String> headers;
/** 是否来自缓存的标志 */
public final boolean notModified;
/** 网络请求的耗时(毫秒) */
public final long networkTimeMs;
// 其他成员变量...
}
9.3 NetworkResponse 类的构造函数
NetworkResponse 类提供了多个构造函数,用于创建不同类型的网络响应:
/**
* 创建一个新的 NetworkResponse 实例
*
* @param statusCode HTTP 状态码
* @param data 响应数据的字节数组
* @param headers 响应头
* @param notModified 是否来自缓存的标志
* @param networkTimeMs 网络请求的耗时(毫秒)
*/
public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
boolean notModified, long networkTimeMs) {
this.statusCode = statusCode;
this.data = data;
this.headers = headers;
this.notModified = notModified;
this.networkTimeMs = networkTimeMs;
}
/**
* 创建一个新的 NetworkResponse 实例,不包含网络请求耗时信息
*
* @param statusCode HTTP 状态码
* @param data 响应数据的字节数组
* @param headers 响应头
* @param notModified 是否来自缓存的标志
*/
public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
boolean notModified) {
this(statusCode, data, headers, notModified, 0);
}
/**
* 创建一个新的 NetworkResponse 实例,使用默认的响应头
*
* @param statusCode HTTP 状态码
* @param data 响应数据的字节数组
*/
public NetworkResponse(int statusCode, byte[] data) {
this(statusCode, data, Collections.<String, String>emptyMap(), false, 0);
}
/**
* 创建一个新的 NetworkResponse 实例,使用默认的状态码和响应头
*
* @param data 响应数据的字节数组
*/
public NetworkResponse(byte[] data) {
this(HttpURLConnection.HTTP_OK, data, Collections.<String, String>emptyMap(), false, 0);
}
/**
* 创建一个新的 NetworkResponse 实例,使用默认的状态码和响应头,并指定是否来自缓存
*
* @param data 响应
9.4 NetworkResponse 类的方法
NetworkResponse 类提供了一些辅助方法,用于处理和分析响应数据:
/**
* 从响应头中提取 Content-Type 信息
*
* @return Content-Type 字符串,如果不存在则返回 null
*/
public String getContentType() {
String contentTypeHeader = headers.get("Content-Type");
if (contentTypeHeader != null) {
// 解析 Content-Type 头,格式通常为 "text/html; charset=utf-8"
String[] parts = contentTypeHeader.split(";");
if (parts.length > 0) {
return parts[0].trim();
}
}
return null;
}
/**
* 从响应头中提取字符集信息
*
* @return 字符集字符串,如果不存在则返回默认字符集 "ISO-8859-1"
*/
public String getCharset() {
String contentTypeHeader = headers.get("Content-Type");
if (contentTypeHeader != null) {
// 解析 Content-Type 头,查找 charset 参数
String[] parts = contentTypeHeader.split(";");
for (String part : parts) {
part = part.trim();
if (part.toLowerCase().startsWith("charset=")) {
return part.substring("charset=".length());
}
}
}
// 默认字符集
return "ISO-8859-1";
}
/**
* 从响应头中提取缓存相关信息,构建缓存条目
*
* @return 缓存条目,如果响应不应该被缓存则返回 null
*/
public Cache.Entry toCacheEntry() {
// 如果响应是 304 Not Modified 且有缓存条目,则直接使用缓存条目
if (notModified && data == null) {
return null;
}
// 提取缓存控制头信息
long now = System.currentTimeMillis();
Map<String, String> headers = this.headers;
String serverEtag = headers.get("ETag");
String headerValue;
// 提取 Date 头
headerValue = headers.get("Date");
long serverDate = parseDateAsEpoch(headerValue);
// 提取 Last-Modified 头
headerValue = headers.get("Last-Modified");
long lastModified = parseDateAsEpoch(headerValue);
// 提取 Expires 头
headerValue = headers.get("Expires");
long serverExpires = parseDateAsEpoch(headerValue);
// 提取 Cache-Control 头(如果有)
String cacheControl = headers.get("Cache-Control");
int maxAge = 0;
boolean mustRevalidate = false;
if (cacheControl != null) {
// 解析 Cache-Control 头
String[] tokens = cacheControl.split(",");
for (String token : tokens) {
token = token.trim().toLowerCase();
if (token.equals("no-cache") || token.equals("no-store")) {
// 明确禁止缓存
return null;
} else if (token.startsWith("max-age=")) {
try {
maxAge = Integer.parseInt(token.substring(8));
} catch (Exception e) {
// 忽略解析错误
}
} else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
mustRevalidate = true;
}
}
}
// 计算缓存的有效期
long softExpire;
long ttl;
if (cacheControl != null && maxAge >= 0) {
// 如果有 Cache-Control: max-age,则使用它
softExpire = now + maxAge * 1000;
ttl = mustRevalidate ? softExpire : softExpire + maxAge * 1000;
} else if (serverDate > 0 && serverExpires >= serverDate) {
// 如果有 Date 和 Expires 头,则使用它们的差值
long age = now - serverDate;
softExpire = now + (serverExpires - serverDate);
ttl = softExpire;
} else {
// 没有足够的信息来计算缓存有效期,不缓存
return null;
}
// 创建缓存条目
Cache.Entry entry = new Cache.Entry();
entry.data = this.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = ttl;
entry.serverDate = serverDate;
entry.lastModified = lastModified;
entry.responseHeaders = headers;
return entry;
}
/**
* 将日期字符串解析为 Unix 时间戳(毫秒)
*
* @param dateStr 日期字符串
* @return Unix 时间戳(毫秒),如果解析失败则返回 0
*/
private static long parseDateAsEpoch(String dateStr) {
if (dateStr == null) {
return 0;
}
try {
// RFC1123 格式的日期解析
return DateUtils.parseDate(dateStr).getTime();
} catch (ParseException e) {
// 解析失败,返回 0
return 0;
}
}
十、NetworkDispatcher 类分析
10.1 NetworkDispatcher 类概述
NetworkDispatcher 是一个线程类,负责从网络请求队列中取出请求并执行。它是 Volley 网络模块的核心调度器,控制着网络请求的执行顺序和并发数量。
10.2 NetworkDispatcher 类的成员变量
下面是 NetworkDispatcher 类的主要成员变量及其作用:
/**
* 网络调度器,负责从网络请求队列中取出请求并执行
*/
public class NetworkDispatcher extends Thread {
/** 请求队列 */
private final BlockingQueue<Request<?>> mQueue;
/** 网络执行器 */
private final Network mNetwork;
/** 缓存存储 */
private final Cache mCache;
/** 响应分发器 */
private final ResponseDelivery mDelivery;
/** 线程停止标志 */
private volatile boolean mQuit = false;
// 其他成员变量...
}
10.3 NetworkDispatcher 类的构造函数
NetworkDispatcher 类的构造函数接收请求队列、网络执行器、缓存存储和响应分发器作为参数:
/**
* 创建一个新的 NetworkDispatcher 实例
*
* @param queue 请求队列
* @param network 网络执行器
* @param cache 缓存存储
* @param delivery 响应分发器
*/
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
10.4 NetworkDispatcher 类的核心方法
NetworkDispatcher 类的核心方法是 run(),它实现了线程的主要逻辑:
/**
* 线程的主运行方法
*/
@Override
public void run() {
// 设置线程名称
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 循环处理请求,直到线程被要求退出
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// 从请求队列中取出一个请求(阻塞操作,直到有请求可用)
request = mQueue.take();
} catch (InterruptedException e) {
// 如果线程被中断,检查是否应该退出
if (mQuit) {
return;
}
continue;
}
try {
// 标记请求已开始
request.addMarker("network-queue-take");
// 如果请求已被取消,不再处理
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
// 检查请求是否需要重试
addTrafficStatsTag(request);
// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果服务器返回 304(Not Modified)且请求有缓存条目,则使用缓存数据
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 解析网络响应
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 如果请求需要缓存,将响应存入缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 标记请求已交付
request.markDelivered();
// 将响应分发到主线程
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
// 处理网络错误
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
// 处理其他异常
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
/**
* 解析并分发网络错误
*
* @param request 请求对象
* @param error 网络错误
*/
private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}
/**
* 为请求添加流量统计标签
*
* @param request 请求对象
*/
private void addTrafficStatsTag(Request<?> request) {
// 如果应用有适当的权限,为请求添加流量统计标签
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
}
/**
* 停止调度器线程
*/
public void quit() {
mQuit = true;
interrupt();
}
十一、网络请求流程分析
11.1 请求的创建与加入队列
当应用程序需要发送一个网络请求时,首先需要创建一个 Request 对象:
// 创建一个 StringRequest 对象,用于获取字符串响应
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// 处理响应
Log.d(TAG, "Response: " + response);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// 处理错误
Log.e(TAG, "Error: " + error.getMessage());
}
});
// 将请求添加到请求队列
requestQueue.add(stringRequest);
RequestQueue 的 add() 方法会对请求进行一些预处理,并将其放入缓存队列或网络队列:
/**
* 将请求添加到请求队列
*
* @param request 请求对象
* @return 返回请求对象本身,便于链式调用
*/
public <T> Request<T> add(Request<T> request) {
// 将请求与当前请求队列关联
request.setRequestQueue(this);
// 添加到正在处理的请求集合中
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// 设置请求的序列号
request.setSequence(getSequenceNumber());
// 设置请求的标签
request.addMarker("add-to-queue");
// 如果请求不需要缓存,直接添加到网络队列
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// 否则,尝试从缓存中获取数据
mCacheQueue.add(request);
return request;
}
11.2 缓存处理流程
当请求被添加到缓存队列后,缓存调度器(CacheDispatcher)会处理这个请求:
/**
* 线程的主运行方法
*/
@Override
public void run() {
// 设置线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 初始化缓存
mCache.initialize();
// 循环处理请求,直到线程被要求退出
while (true) {
try {
// 从缓存队列中取出一个请求(阻塞操作)
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 如果请求已被取消,不再处理
if (request.isCanceled()) {
request.finish("cache-discard-cancelled");
continue;
}
// 尝试从缓存中获取数据
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// 缓存未命中,添加到网络队列
mNetworkQueue.put(request);
continue;
}
// 检查缓存是否已过期
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
// 缓存已过期,添加到网络队列
mNetworkQueue.put(request);
continue;
}
// 缓存命中且未过期
request.addMarker("cache-hit");
// 解析缓存数据
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
// 检查是否需要刷新缓存
if (entry.refreshNeeded()) {
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// 标记响应为中间响应
response.intermediate = true;
// 将请求发送到主线程处理
final Request<?> finalRequest = request;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
// 添加到网络队列,刷新缓存
mNetworkQueue.put(finalRequest);
} catch (InterruptedException e) {
// 线程被中断,恢复中断状态
Thread.currentThread().interrupt();
}
}
});
} else {
// 不需要刷新缓存,直接分发响应
mDelivery.postResponse(request, response);
}
} catch (InterruptedException e) {
// 如果线程被中断,检查是否应该退出
if (mQuit) {
return;
}
continue;
}
}
}
11.3 网络请求处理流程
当请求被添加到网络队列后,网络调度器(NetworkDispatcher)会处理这个请求:
/**
* 线程的主运行方法
*/
@Override
public void run() {
// 设置线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 循环处理请求,直到线程被要求退出
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// 从网络队列中取出一个请求(阻塞操作)
request = mQueue.take();
} catch (InterruptedException e) {
// 如果线程被中断,检查是否应该退出
if (mQuit) {
return;
}
continue;
}
try {
// 标记请求已开始
request.addMarker("network-queue-take");
// 如果请求已被取消,不再处理
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
// 检查请求是否需要重试
addTrafficStatsTag(request);
// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果服务器返回 304(Not Modified)且请求有缓存条目,则使用缓存数据
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 解析网络响应
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 如果请求需要缓存,将响应存入缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 标记请求已交付
request.markDelivered();
// 将响应分发到主线程
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
// 处理网络错误
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
// 处理其他异常
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
11.4 响应分发流程
当网络请求完成后,响应会通过 ResponseDelivery 分发到主线程:
/**
* 将响应分发给主线程
*
* @param request 请求对象
* @param response 响应对象
*/
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
/**
* 将响应分发给主线程,并在分发完成后执行回调
*
* @param request 请求对象
* @param response 响应对象
* @param runnable 回调任务
*/
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
// 标记响应已交付
request.markDelivered();
// 将响应包装到 ResponseDeliveryRunnable 中
ResponseDeliveryRunnable runnable = new ResponseDeliveryRunnable(request, response, runnable);
// 将任务发布到主线程执行
mHandler.post(runnable);
}
/**
* 将错误分发给主线程
*
* @param request 请求对象
* @param error 错误对象
*/
@Override
public void postError(Request<?> request, VolleyError error) {
// 记录错误信息
request.addMarker("post-error");
// 解析错误
Response<?> response = Response.error(error);
// 将错误响应分发给主线程
postResponse(request, response);
}
/**
* 用于在主线程执行的响应分发任务
*/
@SuppressWarnings("rawtypes")
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@Override
public void run() {
// 如果请求已被取消,不处理响应
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// 分发响应
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// 如果响应是中间响应,不执行完成操作
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
// 标记请求已完成
mRequest.finish("done");
}
// 如果有回调任务,执行它
if (mRunnable != null) {
mRunnable.run();
}
}
}
十二、错误处理与重试机制分析
12.1 错误处理机制
Volley 框架提供了完善的错误处理机制,通过 VolleyError 及其子类来表示不同类型的错误:
/**
* Volley 错误的基类
*/
public class VolleyError extends Exception {
// 错误的网络响应
public final NetworkResponse networkResponse;
// 错误发生的时间(毫秒)
private long networkTimeMs;
// 构造函数...
/**
* 获取网络请求耗时
*/
public long getNetworkTimeMs() {
return networkTimeMs;
}
/**
* 设置网络请求耗时
*/
public void setNetworkTimeMs(long networkTimeMs) {
this.networkTimeMs = networkTimeMs;
}
}
/**
* 网络错误,通常表示连接问题
*/
public class NetworkError extends VolleyError {
public NetworkError() {
super();
}
public NetworkError(Throwable cause) {
super(cause);
}
public NetworkError(NetworkResponse networkResponse) {
super(networkResponse);
}
}
/**
* 服务器错误,表示服务器返回了错误状态码
*/
public class ServerError extends VolleyError {
public ServerError(NetworkResponse networkResponse) {
super(networkResponse);
}
}
/**
* 认证失败错误,表示请求需要认证但认证信息不足或无效
*/
public class AuthFailureError extends VolleyError {
public AuthFailureError() {
super();
}
public AuthFailureError(Throwable cause) {
super(cause);
}
public AuthFailureError(NetworkResponse response) {
super(response);
}
public AuthFailureError(String message) {
super(message);
}
public AuthFailureError(String message, Throwable cause) {
super(message, cause);
}
/**
* 获取认证请求头
*
* @return 认证请求头的映射
* @throws AuthFailureError 如果获取认证信息失败
*/
public Map<String, String> getRequestHeaders() throws AuthFailureError {
return null;
}
/**
* 获取认证凭据
*
* @return 认证凭据
* @throws AuthFailureError 如果获取认证信息失败
*/
public byte[] getBody() throws AuthFailureError {
return null;
}
}
/**
* 超时错误,表示请求超时
*/
public class TimeoutError extends VolleyError {
public TimeoutError() {
super();
}
public TimeoutError(NetworkResponse response) {
super(response);
}
}
/**
* 解析错误,表示响应解析失败
*/
public class ParseError extends VolleyError {
public ParseError() {
super();
}
public ParseError(Throwable cause) {
super(cause);
}
public ParseError(NetworkResponse networkResponse) {
super(networkResponse);
}
}
12.2 重试机制
Volley 框架通过 RetryPolicy 接口实现请求的重试机制:
/**
* 重试策略接口,定义了请求重试的基本方法
*/
public interface RetryPolicy {
/**
* 获取当前超时时间(毫秒)
*/
public int getCurrentTimeout();
/**
* 获取当前重试次数
*/
public int getCurrentRetryCount();
/**
* 处理重试
*
* @param error 导致重试的错误
* @throws VolleyError 如果不应该重试,抛出此异常
*/
public void retry(VolleyError error) throws VolleyError;
}
Volley 提供了一个默认的重试策略实现:DefaultRetryPolicy:
/**
* 默认的重试策略实现
*/
public class DefaultRetryPolicy implements RetryPolicy {
/** 默认的初始超时时间(毫秒) */
public static final int DEFAULT_TIMEOUT_MS = 2500;
/** 默认的最大重试次数 */
public static final int DEFAULT_MAX_RETRIES = 1;
/** 默认的退避乘数 */
public static final float DEFAULT_BACKOFF_MULT = 1f;
/** 当前超时时间(毫秒) */
private int mCurrentTimeoutMs;
/** 当前重试次数 */
private int mCurrentRetryCount;
/** 最大重试次数 */
private final int mMaxNumRetries;
/** 退避乘数 */
private final float mBackoffMultiplier;
/**
* 创建一个新的 DefaultRetryPolicy 实例
*
* @param initialTimeoutMs 初始超时时间(毫秒)
* @param maxNumRetries 最大重试次数
* @param backoffMultiplier 退避乘数
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}
/**
* 创建一个使用默认参数的 DefaultRetryPolicy 实例
*/
public DefaultRetryPolicy() {
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}
@Override
public int getCurrentTimeout() {
return mCurrentTimeoutMs;
}
@Override
public int getCurrentRetryCount() {
return mCurrentRetryCount;
}
/**
* 获取退避乘数
*/
public float getBackoffMultiplier() {
return mBackoffMultiplier;
}
@Override
public void retry(VolleyError error) throws VolleyError {
// 增加重试次数
mCurrentRetryCount++;
// 计算新的超时时间
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
// 如果超过最大重试次数,抛出异常
if (!hasAttemptRemaining()) {
throw error;
}
}
/**
* 检查是否还有重试次数
*/
private boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
}
在网络请求过程中,当发生错误时,Volley 会根据重试策略决定是否重试:
/**
* 处理异常并重试请求
*
* @param logPrefix 日志前缀
* @param request 需要重试的请求
* @param exception 导致重试的异常
* @throws VolleyError 如果达到最大重试次数或不应该重试
*/
protected 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));
}
十三、总结
13.1 Volley 网络模块的优势
通过对 Android Volley 网络模块的深入分析,我们可以总结出以下优势:
-
高效的请求调度:Volley 使用线程池和阻塞队列实现了高效的请求调度,可以根据请求的优先级和类型进行合理的处理。
-
灵活的缓存机制:Volley 提供了强大的缓存机制,可以根据 HTTP 响应头自动管理缓存,减少不必要的网络请求,提高应用性能。
-
丰富的错误处理和重试机制:Volley 提供了完善的错误处理机制和灵活的重试策略,可以应对各种网络异常情况,提高请求的成功率。
-
模块化设计:Volley 的网络模块采用了模块化设计,各个组件之间松耦合,可以方便地进行扩展和替换。
-
简化的 API:Volley 提供了简单易用的 API,使开发者可以轻松地发送各种类型的网络请求,处理响应数据。
13.2 Volley 网络模块的局限性
尽管 Volley 网络模块有很多优势,但也存在一些局限性:
-
不适合大数据传输:Volley 主要设计用于小数据量的网络请求,如 JSON、XML 等,如果需要处理大量数据(如文件下载),性能可能不如专门的下载库。
-
缺乏高级功能:Volley 不提供一些高级功能,如断点续传、进度回调等,需要开发者自己实现。
-
对复杂请求支持有限:对于一些复杂的请求,如多部分表单上传,Volley 的支持相对有限,需要进行一些额外的工作。
13.3 使用建议
基于以上分析,对于 Android 开发者,我们提出以下使用建议:
-
适合场景:Volley 非常适合需要频繁进行小数据量网络请求的应用,如社交应用、新闻应用等。
-
配合其他库使用:对于大数据传输或需要高级功能的场景,可以考虑将 Volley 与其他专门的库(如 OkHttp、Retrofit 等)结合使用。
-
自定义扩展:Volley 的模块化设计使其易于扩展,开发者可以根据自己的需求自定义 Network、HttpStack 等组件。
-
合理配置:根据应用的具体需求,合理配置 Volley 的参数,如线程池大小、缓存大小、超时时间等,以获得最佳性能。
通过深入理解 Volley 网络模块的工作原理和实现细节,开发者可以更加灵活和高效地使用 Volley 进行网络通信,提高应用的性能和用户体验。