深入理解Android Volley RequestQueue
一、RequestQueue概述
1.1 RequestQueue的作用
RequestQueue是Volley框架的核心组件之一,负责管理和调度网络请求。它的主要作用包括:
- 管理请求的生命周期
- 调度请求的执行顺序
- 处理请求的缓存逻辑
- 维护并发请求的数量
1.2 RequestQueue的基本架构
RequestQueue由以下几个主要部分组成:
- 缓存队列:存储待处理的缓存请求
- 网络队列:存储待处理的网络请求
- 缓存线程:负责处理缓存请求
- 网络线程池:负责处理网络请求
- 响应分发器:将响应结果分发到主线程
1.3 RequestQueue的优势
- 高效的请求调度机制
- 智能的缓存策略
- 并发请求控制
- 灵活的请求优先级管理
- 响应结果的主线程回调
二、RequestQueue的初始化
2.1 基本初始化方法
// Volley.java
/**
* 创建一个默认配置的RequestQueue实例
* @param context 应用上下文
* @return 初始化后的RequestQueue实例
*/
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
/**
* 创建一个带有自定义HttpStack的RequestQueue实例
* @param context 应用上下文
* @param stack 自定义的HttpStack实现
* @return 初始化后的RequestQueue实例
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
// 创建缓存目录
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
// 如果没有提供自定义HttpStack,则根据Android版本选择合适的实现
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
// 忽略异常,使用默认的userAgent
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
// Android 2.3及以上版本使用HttpURLConnection
stack = new HurlStack();
} else {
// Android 2.2及以下版本使用HttpClient
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
// 创建网络请求处理器
Network network = new BasicNetwork(stack);
// 创建RequestQueue实例并启动
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
2.2 自定义初始化参数
// RequestQueue.java
/**
* 完整参数的构造函数
* @param cache 缓存实现
* @param network 网络请求实现
* @param threadPoolSize 网络线程池大小
* @param delivery 响应分发器
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
// 初始化缓存、网络和分发器
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
/**
* 常用的构造函数,使用默认的响应分发器
* @param cache 缓存实现
* @param network 网络请求实现
* @param threadPoolSize 网络线程池大小
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
// 使用默认的响应分发器,将响应分发到主线程
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
/**
* 最常用的构造函数,使用默认的线程池大小
* @param cache 缓存实现
* @param network 网络请求实现
*/
public RequestQueue(Cache cache, Network network) {
// 默认使用4个网络线程
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
2.3 启动RequestQueue
// RequestQueue.java
/**
* 启动RequestQueue,创建并启动缓存线程和网络线程池
*/
public void start() {
// 停止任何正在运行的分发器
stop();
// 创建并启动缓存分发器
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// 创建并启动网络分发器
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
/**
* 停止RequestQueue,终止所有分发器线程
*/
public void stop() {
// 停止缓存分发器
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
// 停止所有网络分发器
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
三、请求队列的核心组件
3.1 缓存队列(mCacheQueue)
// RequestQueue.java
/** 请求队列使用的缓存队列 */
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
3.2 网络队列(mNetworkQueue)
// RequestQueue.java
/** 请求队列使用的网络队列 */
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
3.3 缓存实现(mCache)
// RequestQueue.java
/** 用于缓存响应的缓存实现 */
private final Cache mCache;
3.4 网络实现(mNetwork)
// RequestQueue.java
/** 用于执行网络请求的网络实现 */
private final Network mNetwork;
3.5 响应分发器(mDelivery)
// RequestQueue.java
/** 用于分发响应的分发器 */
private final ResponseDelivery mDelivery;
3.6 缓存分发器(mCacheDispatcher)
// RequestQueue.java
/** 负责处理缓存请求的线程 */
private CacheDispatcher mCacheDispatcher;
3.7 网络分发器(mDispatchers)
// RequestQueue.java
/** 负责处理网络请求的线程池 */
private NetworkDispatcher[] mDispatchers;
四、请求的添加与处理流程
4.1 添加请求到队列
// RequestQueue.java
/**
* 将请求添加到队列中进行处理
* @param request 要添加的请求
* @return 返回请求本身,便于链式调用
*/
public <T> Request<T> add(Request<T> request) {
// 将请求与当前RequestQueue关联
request.setRequestQueue(this);
// 添加请求到全局请求集合,用于跟踪所有请求
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// 设置请求的序列号
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// 如果请求不需要缓存,则直接添加到网络队列
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// 否则,将请求添加到缓存队列
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
// 检查是否有相同缓存键的请求正在等待
if (mWaitingRequests.containsKey(cacheKey)) {
// 如果有,则将当前请求添加到等待列表
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// 如果没有,则将请求添加到等待列表并开始处理
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
4.2 缓存分发器的工作流程
// CacheDispatcher.java
@Override
public void run() {
if (VolleyLog.DEBUG) VolleyLog.v("start new cache dispatcher");
// 设置线程优先级为后台线程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 初始化缓存
mCache.initialize();
Request<?> request;
while (true) {
try {
// 从缓存队列中取出一个请求(阻塞操作)
request = mCacheQueue.take();
} catch (InterruptedException e) {
// 如果线程被中断,则退出循环
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("cache-queue-take");
// 如果请求已被取消,则跳过处理
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 尝试从缓存中获取数据
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// 缓存未命中,将请求添加到网络队列
mNetworkQueue.add(request);
continue;
}
// 检查缓存是否已过期
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
// 缓存已过期,将请求添加到网络队列
mNetworkQueue.add(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.add(finalRequest);
} catch (Exception e) {
// 忽略异常
}
}
});
} else {
// 不需要刷新缓存,直接将响应发送到主线程
mDelivery.postResponse(request, response);
}
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
}
}
}
4.3 网络分发器的工作流程
// NetworkDispatcher.java
@Override
public void run() {
// 设置线程优先级为后台线程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
try {
// 从网络队列中取出一个请求(阻塞操作)
request = mNetworkQueue.take();
} catch (InterruptedException e) {
// 如果线程被中断,则退出循环
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// 如果请求已被取消,则跳过处理
if (request.isCanceled()) {
request.finish("network-discard-canceled");
continue;
}
// 检查请求是否需要重试
addTrafficStatsTag(request);
// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果服务器返回304状态码(资源未修改),且请求有缓存条目
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() - request.getStartMs());
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - request.getStartMs());
mDelivery.postError(request, volleyError);
}
}
}
五、请求优先级管理
5.1 请求优先级枚举
// Request.java
/**
* 请求优先级枚举,从低到高
*/
public enum Priority {
LOW,
NORMAL,
HIGH,
IMMEDIATE
}
/**
* 获取请求的优先级,默认返回NORMAL
* @return 请求的优先级
*/
public Priority getPriority() {
return Priority.NORMAL;
}
5.2 自定义请求优先级
// 自定义请求示例
public class CustomRequest extends Request<String> {
private final Priority mPriority;
public CustomRequest(int method, String url, Response.Listener<String> listener,
Response.ErrorListener errorListener, Priority priority) {
super(method, url, errorListener);
mPriority = priority;
setListener(listener);
}
@Override
public Priority getPriority() {
return mPriority != null ? mPriority : super.getPriority();
}
// 其他方法实现...
}
// 使用示例
CustomRequest request = new CustomRequest(
Request.Method.GET,
"https://example.com/api/data",
response -> {
// 处理响应
},
error -> {
// 处理错误
},
Request.Priority.HIGH // 设置高优先级
);
// 将请求添加到队列
requestQueue.add(request);
5.3 优先级队列的实现
// PriorityBlockingQueue.java
/**
* PriorityBlockingQueue是一个基于优先级堆的无界阻塞队列
* 元素按照自然顺序或指定的比较器排序
*/
public class PriorityBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
// 内部使用的优先级堆
private transient Object[] queue;
// 队列的大小
private transient int size;
// 比较器,如果为null则使用元素的自然顺序
private transient Comparator<? super E> comparator;
// 用于并发操作的锁
private final ReentrantLock lock;
// 队列非空条件
private final Condition notEmpty;
// 用于分配内部数组的原子变量
private transient volatile int allocationSpinLock;
// 用于序列化的版本UID
private static final long serialVersionUID = 5595510919245408276L;
// 构造函数...
/**
* 将元素插入队列,根据优先级排序
*/
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
int n, cap;
Object[] array;
// 如果队列已满,则扩容
while ((n = size) >= (cap = (array = queue).length))
tryGrow(array, cap);
try {
Comparator<? super E> cmp = comparator;
// 根据比较器插入元素到正确位置
if (cmp == null)
siftUpComparable(n, e, array);
else
siftUpUsingComparator(n, e, array, cmp);
size = n + 1;
notEmpty.signal();
} finally {
lock.unlock();
}
return true;
}
// 其他方法...
}
// RequestQueue中使用PriorityBlockingQueue
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
六、请求的缓存处理
6.1 缓存接口定义
// Cache.java
/**
* Volley使用的缓存接口
*/
public interface Cache {
/**
* 从缓存中获取数据
* @param key 缓存键
* @return 缓存条目,如果不存在则返回null
*/
public Entry get(String key);
/**
* 将数据写入缓存
* @param key 缓存键
* @param entry 缓存条目
*/
public void put(String key, Entry entry);
/**
* 移除指定缓存
* @param key 缓存键
*/
public void invalidate(String key, boolean fullExpire);
/**
* 删除指定缓存
* @param key 缓存键
*/
public void remove(String key);
/**
* 清空所有缓存
*/
public void clear();
/**
* 缓存条目结构
*/
public static class Entry {
/** 缓存数据 */
public byte[] data;
/** HTTP响应头 */
public Map<String, String> responseHeaders;
/** 数据获取时间(从1970年开始的毫秒数) */
public long serverDate;
/** 数据的ETag */
public String etag;
/** 缓存的软过期时间(毫秒) */
public long softTtl;
/** 缓存的绝对过期时间(毫秒) */
public long ttl;
/**
* 判断缓存是否已过期
* @return 如果已过期返回true,否则返回false
*/
public boolean isExpired() {
return this.ttl < System.currentTimeMillis();
}
/**
* 判断缓存是否需要刷新
* @return 如果需要刷新返回true,否则返回false
*/
public boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}
}
}
6.2 磁盘缓存实现
// DiskBasedCache.java
/**
* 基于磁盘的缓存实现
*/
public class DiskBasedCache implements Cache {
/** 默认缓存目录名 */
private static final String DEFAULT_CACHE_DIR = "volley";
/** 缓存目录 */
private final File mRootDirectory;
/** 缓存的最大大小(字节) */
private final int mMaxCacheSizeInBytes;
/** 当前缓存大小(字节) */
private int mTotalSize = 0;
/** 缓存条目映射 */
private final HashMap<String, CacheHeader> mEntries = new HashMap<String, CacheHeader>();
/**
* 缓存条目头部信息,用于存储元数据
*/
static class CacheHeader {
/** 缓存键 */
public String key;
/** 缓存数据的ETag */
public String etag;
/** 服务器响应时间(从1970年开始的毫秒数) */
public long serverDate;
/** 数据最后修改时间 */
public long lastModified;
/** 缓存的软过期时间 */
public long softTtl;
/** 缓存的绝对过期时间 */
public long ttl;
/** 响应头 */
public Map<String, String> responseHeaders;
// 构造函数和序列化/反序列化方法...
/**
* 从文件读取缓存头部信息
* @param file 缓存文件
* @return 解析后的缓存头部信息
*/
public static CacheHeader readHeader(File file) {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
// 读取缓存头部信息
CacheHeader entry = new CacheHeader();
entry.key = dis.readUTF();
entry.etag = dis.readUTF();
entry.serverDate = dis.readLong();
entry.lastModified = dis.readLong();
entry.softTtl = dis.readLong();
entry.ttl = dis.readLong();
// 读取响应头数量
int headerCount = dis.readInt();
entry.responseHeaders = new HashMap<String, String>(headerCount);
// 读取所有响应头
for (int i = 0; i < headerCount; i++) {
entry.responseHeaders.put(dis.readUTF(), dis.readUTF());
}
return entry;
} catch (IOException e) {
VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
if (fis != null) {
try {
fis.close();
} catch (IOException ignored) {
}
}
return null;
}
}
/**
* 将缓存头部信息写入文件
* @param file 目标文件
*/
public boolean writeHeader(File file) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
DataOutputStream dos = new DataOutputStream(fos);
dos.writeUTF(key);
dos.writeUTF(etag);
dos.writeLong(serverDate);
dos.writeLong(lastModified);
dos.writeLong(softTtl);
dos.writeLong(ttl);
// 写入响应头数量
dos.writeInt(responseHeaders.size());
// 写入所有响应头
for (Map.Entry<String, String> header : responseHeaders.entrySet()) {
dos.writeUTF(header.getKey());
dos.writeUTF(header.getValue());
}
return true;
} catch (IOException e) {
VolleyLog.d(e, "Error writing header for %s", file.getAbsolutePath());
return false;
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException ignored) {
}
}
}
}
/**
* 从缓存头部信息创建完整的缓存条目
* @param data 缓存数据
* @return 缓存条目
*/
public Entry toCacheEntry(byte[] data) {
Entry e = new Entry();
e.data = data;
e.etag = etag;
e.serverDate = serverDate;
e.lastModified = lastModified;
e.softTtl = softTtl;
e.ttl = ttl;
e.responseHeaders = responseHeaders;
return e;
}
}
// 其他方法实现...
}
6.3 缓存处理流程
// CacheDispatcher.java
// 缓存分发器处理请求的主要逻辑
while (true) {
try {
// 从缓存队列获取请求
request = mCacheQueue.take();
} catch (InterruptedException e) {
// 处理中断...
}
try {
// 检查请求是否已取消
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 尝试从缓存获取数据
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
// 缓存未命中,添加到网络队列
request.addMarker("cache-miss");
mNetworkQueue.add(request);
continue;
}
// 检查缓存是否已过期
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.add(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.add(finalRequest);
} catch (Exception e) {
// 忽略异常
}
}
});
} else {
// 不需要刷新,直接发送响应
mDelivery.postResponse(request, response);
}
} catch (Exception e) {
// 处理异常...
}
}
七、请求的并发控制
7.1 网络线程池大小设置
// RequestQueue.java
/** 默认的网络线程池大小 */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/**
* 使用默认参数创建RequestQueue
* @param cache 缓存实现
* @param network 网络实现
*/
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
/**
* 使用指定的线程池大小创建RequestQueue
* @param cache 缓存实现
* @param network 网络实现
* @param threadPoolSize 网络线程池大小
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
7.2 并发请求的执行
// RequestQueue.java
/**
* 启动RequestQueue,创建并启动缓存和网络分发器
*/
public void start() {
// 停止任何正在运行的分发器
stop();
// 创建并启动缓存分发器
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// 创建并启动网络分发器
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
// NetworkDispatcher.java
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
try {
// 从网络队列获取请求(阻塞操作)
request = mNetworkQueue.take();
} catch (InterruptedException e) {
// 处理中断...
}
try {
// 检查请求是否已取消
if (request.isCanceled()) {
request.finish("network-discard-canceled");
continue;
}
// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
// 处理响应...
} catch (VolleyError volleyError) {
// 处理错误...
} catch (Exception e) {
// 处理异常...
}
}
}
7.3 并发控制策略
// 可以通过调整线程池大小来控制并发请求数量
RequestQueue requestQueue = Volley.newRequestQueue(context, 8); // 设置8个网络线程
// 也可以通过自定义RequestQueue来实现更复杂的并发控制
public class CustomRequestQueue extends RequestQueue {
private final Semaphore mSemaphore;
public CustomRequestQueue(Cache cache, Network network, int maxConcurrentRequests) {
super(cache, network, maxConcurrentRequests);
// 使用信号量控制最大并发请求数
mSemaphore = new Semaphore(maxConcurrentRequests);
}
@Override
public <T> Request<T> add(Request<T> request) {
try {
// 获取许可
mSemaphore.acquire();
} catch (InterruptedException e) {
// 处理中断
return null;
}
// 请求完成后释放许可
request.setRequestFinishedListener(new RequestFinishedListener<Object>() {
@Override
public void onRequestFinished(Request<Object> request) {
mSemaphore.release();
}
});
return super.add(request);
}
}
八、响应分发机制
8.1 响应分发接口
// ResponseDelivery.java
/**
* 响应分发接口,定义了如何将响应分发到主线程
*/
public interface ResponseDelivery {
/**
* 将响应分发到主线程
* @param request 请求对象
* @param response 响应对象
*/
public void postResponse(Request<?> request, Response<?> response);
/**
* 将响应分发到主线程,并在分发完成后执行回调
* @param request 请求对象
* @param response 响应对象
* @param runnable 回调任务
*/
public void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/**
* 将错误分发到主线程
* @param request 请求对象
* @param error 错误对象
*/
public void postError(Request<?> request, VolleyError error);
}
8.2 默认响应分发器实现
// ExecutorDelivery.java
/**
* 默认的响应分发器实现,使用Executor将响应分发到主线程
*/
public class ExecutorDelivery implements ResponseDelivery {
/** 用于分发响应的Executor */
private final Executor mResponsePoster;
/**
* 创建一个在主线程分发响应的响应分发器
* @param handler 主线程的Handler
*/
public ExecutorDelivery(final Handler handler) {
// 创建一个在Handler所在线程执行的Executor
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
/**
* 创建一个使用自定义Executor的响应分发器
* @param executor 自定义的Executor
*/
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
// 标记请求已分发响应
request.markDelivered();
request.addMarker("post-response");
// 将响应分发任务提交到Executor
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
/**
* 用于在Executor线程执行的响应分发任务
*/
@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;
}
@SuppressWarnings("unchecked")
@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();
}
}
}
}
8.3 请求的响应回调
// Request.java
/**
* 将响应结果分发到主线程的回调
* @param response 响应结果
*/
public void deliverResponse(T response) {
if (mListener != null) {
mListener.onResponse(response);
}
}
/**
* 将错误结果分发到主线程的回调
* @param error 错误对象
*/
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
九、请求的生命周期管理
9.1 请求状态管理
// Request.java
/** 请求的当前状态 */
private enum State {
PENDING, // 等待处理
RUNNING, // 正在处理
FINISHED // 已完成
}
/** 当前状态 */
private State mState = State.PENDING;
/** 请求是否已取消 */
private boolean mCanceled = false;
/** 请求是否已分发响应 */
private boolean mResponseDelivered = false;
/**
* 取消请求
*/
public void cancel() {
synchronized (this) {
mCanceled = true;
mState = State.FINISHED;
}
}
/**
* 检查请求是否已取消
* @return 如果已取消返回true,否则返回false
*/
public boolean isCanceled() {
synchronized (this) {
return mCanceled;
}
}
/**
* 标记请求已完成
* @param tag 完成标记
*/
public void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
}
mEventLog.add(tag, Thread.currentThread().getId());
mResponseDelivered = true;
// 释放请求持有的资源
onFinish();
}
/**
* 请求完成时调用的方法,子类可重写
*/
protected void onFinish() {
// 释放请求持有的资源
}
/**
* 标记响应已分发
*/
public void markDelivered() {
synchronized (this) {
mResponseDelivered = true;
}
}
/**
* 检查响应是否已分发
* @return 如果已分发返回true,否则返回false
*/
public boolean hasHadResponseDelivered() {
synchronized (this) {
return mResponseDelivered;
}
}
9.2 请求的完成处理
// RequestQueue.java
/**
* 标记请求已完成,并从当前请求集合中移除
* @param request 已完成的请求
*/
<T> void finish(Request<T> request) {
// 从当前请求集合中移除
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
// 如果请求在等待队列中,则也从等待队列中移除
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.get(cacheKey);
if (waitingRequests != null) {
// 如果该缓存键有等待的请求
if (waitingRequests.remove(request)) {
// 如果当前请求在等待队列中,且已被移除
// 如果等待队列为空,则从等待映射中移除
if (waitingRequests.isEmpty()) {
mWaitingRequests.remove(cacheKey);
}
// 如果请求不需要缓存,则没有必要继续处理等待的请求
if (!request.shouldCache()) {
return;
}
// 启动下一个等待的请求
startNextWaitingRequest(cacheKey);
}
}
}
}
/**
* 启动下一个等待的请求
* @param cacheKey 缓存键
*/
private void startNextWaitingRequest(String cacheKey) {
Queue<Request<?>> waitingRequests;
synchronized (mWaitingRequests) {
waitingRequests = mWaitingRequests.get(cacheKey);
if (waitingRequests == null || waitingRequests.isEmpty()) {
return;
}
}
// 将第一个请求移到网络队列
Request<?> request = waitingRequests.poll();
if (waitingRequests.isEmpty()) {
mWaitingRequests.remove(cacheKey);
}
// 将请求添加到网络队列
request.addMarker("waiting-to-queue");
addToNetworkQueue(request);
}
十、请求的统计与监控
10.1 请求时间统计
// Request.java
/** 事件日志,用于记录请求处理过程中的时间点 */
private final EventLog mEventLog = EventLog.obtain();
/** 请求开始时间(毫秒) */
private long mRequestStart = SystemClock.elapsedRealtime();
/**
* 添加一个事件标记和时间戳
* @param tag 事件标签
*/
public void addMarker(String tag) {
mEventLog.add(tag, Thread.currentThread().getId());
}
/**
* 获取请求的开始时间
* @return 开始时间(毫秒)
*/
public long getStartMs() {
return mRequestStart;
}
// EventLog.java
/**
* 轻量级事件日志,用于记录请求处理过程中的时间点
*/
public class EventLog {
private static final int MAX_EVENTS = 20;
// 事件标签数组
private String[] mTags;
// 事件时间戳数组
private long[] mTimes;
// 事件线程ID数组
private long[] mThreadIds;
// 当前事件数量
private int mCount;
// 私有构造函数
private EventLog() {
mTags = new String[MAX_EVENTS];
mTimes = new long[MAX_EVENTS];
mThreadIds = new long[MAX_EVENTS];
mCount = 0;
}
/**
* 获取一个EventLog实例
* @return EventLog实例
*/
public static EventLog obtain() {
return new EventLog();
}
/**
* 添加一个事件
* @param tag 事件标签
* @param threadId 线程ID
*/
public void add(String tag, long threadId) {
// 如果事件数量超过最大值,则移除最旧的事件
if (mCount >= MAX_EVENTS) {
System.arraycopy(mTags, 1, mTags, 0, MAX_EVENTS - 1);
System.arraycopy(mTimes, 1, mTimes, 0, MAX_EVENTS - 1);
System.arraycopy(mThreadIds, 1, mThreadIds, 0, MAX_EVENTS - 1);
mCount--;
}
// 添加新事件
mTags[mCount] = tag;
mTimes[mCount] = SystemClock.elapsedRealtime();
mThreadIds[mCount] = threadId;
mCount++;
}
// 其他方法...
}
10.2 请求监控与调试
// RequestQueue.java
/**
* 打印所有当前请求的调试信息
*/
public void dump() {
VolleyLog.v("Current requests:");
if (mCurrentRequests.isEmpty()) {
VolleyLog.v(" None");
return;
}
// 按时间排序请求
List<Request<?>> copy = new ArrayList<Request<?>>(mCurrentRequests);
Collections.sort(copy, new Comparator<Request<?>>() {
@Override
public int compare(Request<?> lhs, Request<?> rhs) {
return (int) (lhs.getStartMs() - rhs.getStartMs());
}
});
// 打印每个请求的调试信息
for (Request<?> request : copy) {
VolleyLog.v(" %s", request.toString());
}
}
// Request.java
/**
* 获取请求的调试信息
* @return 调试信息字符串
*/
@Override
public String toString() {
String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag());
return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " "
+ (mResponseDelivered ? "(delivered)" : "")
+ (hasHadResponseDelivered() ? "(done)" : "")
+ " " + getClass().getSimpleName()
+ " " + Priority.toString(getPriority())
+ " " + (SystemClock.elapsedRealtime() - mRequestStart) + "ms";
}
10.3 性能监控工具集成
// 可以通过自定义RequestQueue来集成性能监控工具
public class MonitoredRequestQueue extends RequestQueue {
private final PerformanceMonitor mPerformanceMonitor;
public MonitoredRequestQueue(Cache cache, Network network, PerformanceMonitor monitor) {
super(cache, network);
mPerformanceMonitor = monitor;
}
@Override
public <T> Request<T> add(Request<T> request) {
// 在请求开始时记录
mPerformanceMonitor.onRequestStarted(request);
// 设置请求完成监听器
request.setRequestFinishedListener(new RequestFinishedListener<Object>() {
@Override
public void onRequestFinished(Request<Object> request) {
// 在请求完成时记录
mPerformanceMonitor.onRequestFinished(request);
}
});
return super.add(request);
}
}
// 性能监控接口示例
public interface PerformanceMonitor {
void onRequestStarted(Request<?> request);
void onRequestFinished(Request<?> request);
void onNetworkRequestStarted(Request<?> request);
void onNetworkRequestCompleted(Request<?> request, NetworkResponse response);
void onNetworkRequestFailed(Request<?> request, VolleyError error);
}
十一、RequestQueue的高级应用
11.1 自定义RequestQueue配置
// 创建自定义配置的RequestQueue
public class CustomVolley {
private static RequestQueue sRequestQueue;
public static RequestQueue getRequestQueue(Context context) {
if (sRequestQueue == null) {
// 创建缓存目录
File cacheDir = new File(context.getCacheDir(), "custom-volley-cache");
// 创建自定义HttpStack
HttpStack stack;
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance("custom-user-agent"));
}
// 创建网络实现
Network network = new BasicNetwork(stack);
// 创建磁盘缓存(10MB)
DiskBasedCache cache = new DiskBasedCache(cacheDir, 10 * 1024 * 1024);
// 创建自定义RequestQueue(使用6个网络线程)
sRequestQueue = new RequestQueue(cache, network, 6);
// 启动RequestQueue
sRequestQueue.start();
}
return sRequestQueue;
}
}
11.2 批量请求处理
// 批量请求处理示例
public class BatchRequestProcessor {
private final RequestQueue mRequestQueue;
private final List<Request<?>> mRequests = new ArrayList<>();
private final CountDownLatch mCountDownLatch;
private final BatchListener mListener;
public interface BatchListener {
void onBatchCompleted(List<Request<?>> successfulRequests,
List<Request<?>> failedRequests);
}
public BatchRequestProcessor(RequestQueue requestQueue, int requestCount,
BatchListener listener) {
mRequestQueue = requestQueue;
mCountDownLatch = new CountDownLatch(requestCount);
mListener = listener;
}
public void addRequest(Request<?> request) {
mRequests.add(request);
// 设置请求完成监听器
request.setRequestFinishedListener(new RequestFinishedListener<Object>() {
@Override
public void onRequestFinished(Request<Object> request) {
// 减少计数器
mCountDownLatch.countDown();
// 检查所有请求是否已完成
if (mCountDownLatch.getCount() == 0) {
// 分离成功和失败的请求
List<Request<?>> successfulRequests = new ArrayList<>();
List<Request<?>> failedRequests = new ArrayList<>();
for (Request<?> req : mRequests) {
if (req.hasHadResponseDelivered() && !req.isCanceled()) {
successfulRequests.add(req);
} else {
failedRequests.add(req);
}
}
// 回调结果
if (mListener != null) {
mListener.onBatchCompleted(successfulRequests, failedRequests);
}
}
}
});
// 将请求添加到队列
mRequestQueue.add(request);
}
}
// 使用示例
BatchRequestProcessor processor = new BatchRequestProcessor(requestQueue, 3,
new BatchRequestProcessor.BatchListener() {
@Override
public void onBatchCompleted(List<Request<?>> successfulRequests,
List<Request<?>> failedRequests) {
Log.d(TAG, "Batch completed: " + successfulRequests.size() + " succeeded, "
+ failedRequests.size() + " failed");
}
});
// 添加请求
processor.addRequest(request1);
processor.addRequest(request2);
processor.addRequest(request3);
11.3 请求优先级控制
// 优先级控制示例
public void sendPrioritizedRequests() {
RequestQueue requestQueue = Volley.newRequestQueue(context);
// 创建高优先级请求
Request<?> highPriorityRequest = new StringRequest(
Request.Method.GET,
"https://example.com/api/high-priority",
response -> {
// 处理响应
},
error -> {
// 处理错误
}
) {
@Override
public Priority getPriority() {
return Priority.HIGH;
}
};
// 创建低优先级请求
Request<?> lowPriorityRequest = new StringRequest(
Request.Method.GET,
"https://example.com/api/low-priority",
response -> {
// 处理响应
},
error -> {
// 处理错误
}
) {
@Override
public Priority getPriority() {
return Priority.LOW;
}
};
// 添加请求到队列
requestQueue.add(highPriorityRequest);
requestQueue.add(lowPriorityRequest);
}
十二、RequestQueue的常见问题与解决方案
12.1 请求队列阻塞问题
问题描述:请求队列中的请求长时间未被处理,导致界面卡顿。
可能原因:
- 网络线程池大小不足
- 某个请求耗时过长,阻塞了队列
- 缓存线程被阻塞
解决方案:
- 增加网络线程池大小
RequestQueue requestQueue = new RequestQueue(cache, network, 8); // 使用8个网络线程
- 检查耗时过长的请求并优化
- 确保缓存操作高效,避免阻塞缓存线程
12.2 内存泄漏问题
问题描述:应用中出现内存泄漏,导致内存占用过高。
可能原因:
- 请求持有Activity或其他上下文的强引用
- 请求完成后未及时释放资源
- 未正确取消不再需要的请求
解决方案:
- 使用弱引用持有上下文
public class MyRequest extends Request<String> {
private WeakReference<Context> mContextRef;
public MyRequest(Context context, String url, Listener<String> listener,
ErrorListener errorListener) {
super(Method.GET, url, errorListener);
mContextRef = new WeakReference<>(context);
setListener(listener);
}
// 其他方法...
}
- 在请求的onFinish()方法中释放资源
@Override
protected void onFinish() {
super.onFinish();
// 释放持有的资源
}
- 在Activity销毁时取消相关请求
@Override
protected void onDestroy() {
super.onDestroy();
if (requestQueue != null) {
requestQueue.cancelAll(this);
}
}
12.3 缓存不一致问题
问题描述:缓存数据与服务器数据不一致,导致显示旧数据。
可能原因:
- 缓存策略设置不当
- 服务器数据更新后,客户端未及时刷新缓存
- 缓存键设置不合理,导致数据混淆
解决方案:
- 调整缓存策略
request.setShouldCache(true); // 启用缓存
request.setCacheEntry(new Cache.Entry() {
@Override
public boolean isExpired() {
// 自定义过期判断逻辑
return false;
}
@Override
public boolean refreshNeeded() {
// 自定义刷新判断逻辑
return true;
}
});
- 手动刷新缓存
// 清除特定缓存
requestQueue.getCache().invalidate("cache-key", true);
// 或清空所有缓存
requestQueue.getCache().clear();
- 使用唯一且合理的缓存键
@Override
public String getCacheKey() {
return getUrl() + "-" + getParams(); // 使用URL和参数组合作为缓存键
}
12.4 多线程并发问题
问题描述:在多线程环境下,RequestQueue出现异常或数据不一致。
可能原因:
- 多个线程同时操作RequestQueue
- 请求状态在多线程环境下未正确同步
- 共享资源未加锁保护
解决方案:
- 在单线程中操作RequestQueue
// 使用Handler在主线程操作RequestQueue
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(() -> {
requestQueue.add(request);
});
- 确保请求状态同步
public synchronized void updateRequestStatus(Request<?> request, Status status) {
// 更新请求状态的同步方法
}
- 对共享资源加锁
private final Object mLock = new Object();
public void processSharedResource() {
synchronized (mLock) {
// 处理共享资源的代码
}
}
12.5 网络请求超时问题
问题描述:在网络状况不佳的情况下,请求经常超时失败。
可能原因:
- 默认超时时间设置过短
- 服务器响应时间过长
- 网络波动导致请求延迟
解决方案:
- 增加请求超时时间
// 设置请求超时时间为30秒(默认通常为5秒)
request.setRetryPolicy(new DefaultRetryPolicy(
30000, // 初始超时时间(毫秒)
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, // 最大重试次数
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT // 重试延迟倍数
));
- 实现合理的重试策略
// 创建自定义重试策略
RetryPolicy retryPolicy = new DefaultRetryPolicy(
15000, // 超时时间15秒
3, // 最多重试3次
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT // 重试延迟倍数
);
request.setRetryPolicy(retryPolicy);
- 考虑使用异步请求避免UI卡顿
12.6 大文件下载问题
问题描述:使用Volley下载大文件时出现内存溢出或下载失败。
可能原因:
- Volley默认将整个响应加载到内存中
- 大文件下载过程中内存占用过高
- 没有实现断点续传机制
解决方案:
- 使用自定义Request实现流式下载
public class FileDownloadRequest extends Request<File> {
private final File mDestinationFile;
private final Listener<File> mListener;
public FileDownloadRequest(String url, File destinationFile,
Listener<File> listener, ErrorListener errorListener) {
super(Method.GET, url, errorListener);
mDestinationFile = destinationFile;
mListener = listener;
}
@Override
protected Response<File> parseNetworkResponse(NetworkResponse response) {
try {
// 将响应数据写入文件
FileOutputStream fos = new FileOutputStream(mDestinationFile);
fos.write(response.data);
fos.close();
return Response.success(mDestinationFile, HttpHeaderParser.parseCacheHeaders(response));
} catch (IOException e) {
return Response.error(new VolleyError(e));
}
}
@Override
protected void deliverResponse(File response) {
mListener.onResponse(response);
}
}
- 实现断点续传功能
// 设置Range头实现断点续传
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = super.getHeaders();
if (mDestinationFile.exists()) {
headers.put("Range", "bytes=" + mDestinationFile.length() + "-");
}
return headers;
}
- 考虑使用更适合大文件下载的库如OkHttp
12.7 请求重复发送问题
问题描述:相同请求被多次发送,浪费网络资源。
可能原因:
- 界面刷新频繁触发重复请求
- 没有正确管理请求生命周期
- 没有实现请求去重机制
解决方案:
- 使用请求标记管理请求
// 设置请求标记
request.setTag("unique_tag");
// 取消相同标记的请求
requestQueue.cancelAll("unique_tag");
- 实现请求去重逻辑
// 在添加请求前检查是否已有相同请求
private boolean hasRequestWithUrl(String url) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (request.getUrl().equals(url)) {
return true;
}
}
}
return false;
}
- 使用缓存机制避免重复请求
十三、RequestQueue的性能优化技巧
13.1 合理配置线程池大小
根据应用的网络请求特性和设备性能,合理调整网络线程池大小:
- 对于大多数应用,默认的4个线程已经足够
- 对于需要大量并发请求的应用,可以增加到6-8个线程
- 过多的线程会导致上下文切换开销增加,反而降低性能
// 创建具有8个网络线程的RequestQueue
RequestQueue requestQueue = new RequestQueue(cache, network, 8);
13.2 优化缓存策略
根据数据更新频率和重要性,设置不同的缓存策略:
- 对于静态数据(如应用配置),设置较长的缓存时间
- 对于动态数据(如用户信息),设置较短的缓存时间或禁用缓存
- 实现智能缓存刷新机制
// 设置自定义缓存时间
public class CustomRequest extends Request<String> {
// ... 其他方法
@Override
public Priority getPriority() {
return Priority.HIGH;
}
@Override
public Cache.Entry parseNetworkResponse(NetworkResponse response) {
Cache.Entry entry = super.parseNetworkResponse(response);
if (entry != null) {
// 设置缓存10分钟后过期
entry.softTtl = System.currentTimeMillis() + 10 * 60 * 1000;
// 设置缓存1小时后必须刷新
entry.ttl = System.currentTimeMillis() + 60 * 60 * 1000;
}
return entry;
}
}
13.3 使用压缩请求和响应
启用GZIP压缩可以显著减少数据传输量,提高请求响应速度:
// 添加GZIP请求头
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = super.getHeaders();
if (headers == null || headers.isEmpty()) {
headers = new HashMap<String, String>();
}
headers.put("Accept-Encoding", "gzip");
return headers;
}
13.4 实现请求批处理
对于需要同时发送多个请求的场景,考虑实现请求批处理:
// 批量请求处理器
public class BatchRequestProcessor {
private final RequestQueue mRequestQueue;
private final List<Request<?>> mRequests = new ArrayList<>();
private final CountDownLatch mCountDownLatch;
private final BatchListener mListener;
public interface BatchListener {
void onBatchCompleted(List<Request<?>> successfulRequests,
List<Request<?>> failedRequests);
}
public BatchRequestProcessor(RequestQueue requestQueue, int requestCount,
BatchListener listener) {
mRequestQueue = requestQueue;
mCountDownLatch = new CountDownLatch(requestCount);
mListener = listener;
}
// 添加请求并设置完成监听器
public void addRequest(Request<?> request) {
mRequests.add(request);
request.setRequestFinishedListener(new RequestFinishedListener<Object>() {
@Override
public void onRequestFinished(Request<Object> request) {
mCountDownLatch.countDown();
if (mCountDownLatch.getCount() == 0) {
List<Request<?>> successful = new ArrayList<>();
List<Request<?>> failed = new ArrayList<>();
for (Request<?> req : mRequests) {
if (req.hasHadResponseDelivered() && !req.isCanceled()) {
successful.add(req);
} else {
failed.add(req);
}
}
if (mListener != null) {
mListener.onBatchCompleted(successful, failed);
}
}
}
});
mRequestQueue.add(request);
}
}
13.5 优化响应数据解析
对于JSON或XML等结构化数据,使用高效的解析库(如Gson、Jackson):
// 使用Gson解析JSON响应
public class GsonRequest<T> extends Request<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Listener<T> listener;
/**
* 构建一个新的Gson请求
* @param method HTTP请求方法
* @param url 请求URL
* @param clazz 响应数据类型
* @param headers 请求头
* @param listener 响应监听器
* @param errorListener 错误监听器
*/
public GsonRequest(int method, String url, Class<T> clazz, Map<String, String> headers,
Listener<T> listener, ErrorListener errorListener) {
super(method, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.listener = listener;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(
response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(
gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
@Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}
}
十四、RequestQueue在不同场景下的最佳实践
14.1 应用启动时的批量数据加载
在应用启动时,通常需要加载多个配置或初始化数据:
- 使用请求优先级确保关键数据优先加载
- 实现请求批处理,在所有数据加载完成后通知UI
- 使用缓存机制避免重复加载相同数据
// 应用启动时的数据加载示例
public void loadInitialData() {
// 创建高优先级请求获取用户信息
Request userRequest = new StringRequest(
Request.Method.GET,
"https://api.example.com/user",
response -> {
// 处理用户信息
},
error -> {
// 处理错误
}
) {
@Override
public Priority getPriority() {
return Priority.HIGH;
}
};
// 创建中等优先级请求获取配置信息
Request configRequest = new StringRequest(
Request.Method.GET,
"https://api.example.com/config",
response -> {
// 处理配置信息
},
error -> {
// 处理错误
}
) {
@Override
public Priority getPriority() {
return Priority.NORMAL;
}
};
// 使用批处理处理器
BatchRequestProcessor processor = new BatchRequestProcessor(
requestQueue,
2,
(successfulRequests, failedRequests) -> {
if (failedRequests.isEmpty()) {
// 所有请求成功完成,通知UI
handler.post(() -> {
// 更新UI
});
} else {
// 处理失败的请求
}
}
);
processor.addRequest(userRequest);
processor.addRequest(configRequest);
}
14.2 列表数据的分页加载
在实现列表数据的分页加载时:
- 使用唯一的缓存键区分不同页的数据
- 实现加载更多时的请求管理
- 处理列表刷新和加载更多的不同场景
// 列表数据分页加载示例
public class PagedDataLoader {
private static final int PAGE_SIZE = 20;
private int mCurrentPage = 0;
private boolean mIsLoading = false;
private boolean mHasMoreData = true;
private final RequestQueue mRequestQueue;
private final String mBaseUrl;
private final Response.Listener<String> mSuccessListener;
private final Response.ErrorListener mErrorListener;
public PagedDataLoader(RequestQueue requestQueue, String baseUrl,
Response.Listener<String> successListener,
Response.ErrorListener errorListener) {
mRequestQueue = requestQueue;
mBaseUrl = baseUrl;
mSuccessListener = successListener;
mErrorListener = errorListener;
}
// 加载第一页数据(刷新)
public void refresh() {
mCurrentPage = 0;
mHasMoreData = true;
loadPage(mCurrentPage);
}
// 加载下一页数据
public void loadMore() {
if (!mIsLoading && mHasMoreData) {
loadPage(mCurrentPage + 1);
}
}
private void loadPage(int page) {
mIsLoading = true;
// 构建请求URL,包含分页参数
String url = mBaseUrl + "?page=" + page + "&size=" + PAGE_SIZE;
StringRequest request = new StringRequest(
Request.Method.GET,
url,
response -> {
mIsLoading = false;
// 解析响应数据
List<DataItem> items = parseResponse(response);
// 判断是否还有更多数据
if (items.size() < PAGE_SIZE) {
mHasMoreData = false;
} else {
mCurrentPage = page;
}
// 通知成功加载
if (mSuccessListener != null) {
mSuccessListener.onResponse(response);
}
},
error -> {
mIsLoading = false;
// 通知加载失败
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
) {
@Override
public String getCacheKey() {
// 使用URL作为缓存键,确保不同页的数据有不同的缓存
return url;
}
};
// 添加到请求队列
mRequestQueue.add(request);
}
// 解析响应数据的方法
private List<DataItem> parseResponse(String response) {
// 解析JSON数据
return new ArrayList<>();
}
// 检查是否正在加载
public boolean isLoading() {
return mIsLoading;
}
// 检查是否还有更多数据
public boolean hasMoreData() {
return mHasMoreData;
}
}
14.3 实时数据更新
对于需要实时更新的数据:
- 实现长连接或轮询机制
- 设置适当的缓存策略,避免频繁请求相同数据
- 使用WebSocket或Server-Sent Events替代传统HTTP请求
// 实时数据更新示例
public class RealTimeDataManager {
private static final int POLLING_INTERVAL = 30000; // 30秒轮询间隔
private final RequestQueue mRequestQueue;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final String mDataUrl;
private final Response.Listener<String> mListener;
private final Response.ErrorListener mErrorListener;
private StringRequest mCurrentRequest;
private boolean mIsPolling = false;
public RealTimeDataManager(RequestQueue requestQueue, String dataUrl,
Response.Listener<String> listener,
Response.ErrorListener errorListener) {
mRequestQueue = requestQueue;
mDataUrl = dataUrl;
mListener = listener;
mErrorListener = errorListener;
}
// 开始轮询
public void startPolling() {
if (!mIsPolling) {
mIsPolling = true;
fetchData();
}
}
// 停止轮询
public void stopPolling() {
mIsPolling = false;
if (mCurrentRequest != null) {
mCurrentRequest.cancel();
mCurrentRequest = null;
}
mHandler.removeCallbacksAndMessages(null);
}
// 获取数据
private void fetchData() {
if (!mIsPolling) {
return;
}
// 取消当前请求(如果有)
if (mCurrentRequest != null) {
mCurrentRequest.cancel();
}
// 创建新请求
mCurrentRequest = new StringRequest(
Request.Method.GET,
mDataUrl,
response -> {
mCurrentRequest = null;
// 处理响应
if (mListener != null) {
mListener.onResponse(response);
}
// 安排下一次轮询
mHandler.postDelayed(this::fetchData, POLLING_INTERVAL);
},
error -> {
mCurrentRequest = null;
// 处理错误
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
// 安排下一次轮询,即使本次失败
mHandler.postDelayed(this::fetchData, POLLING_INTERVAL);
}
) {
@Override
public Priority getPriority() {
return Priority.LOW; // 轮询请求优先级较低
}
@Override
public String getCacheKey() {
// 添加时间戳确保每次请求都不使用缓存
return super.getCacheKey() + "_" + System.currentTimeMillis();
}
};
// 添加到请求队列
mRequestQueue.add(mCurrentRequest);
}
}
14.4 图片加载与缓存
虽然Volley内置了ImageLoader,但对于复杂的图片加载需求,建议使用专门的图片加载库:
- 对于简单场景,可以使用Volley的ImageRequest
- 对于复杂场景,推荐使用Glide、Picasso或Fresco
- 实现图片缓存清理策略,避免占用过多存储空间
// 使用Volley加载图片示例
public class ImageLoaderExample {
private final RequestQueue mRequestQueue;
private final ImageLoader mImageLoader;
private final LruCache<String, Bitmap> mImageCache;
public ImageLoaderExample(Context context) {
mRequestQueue = Volley.newRequestQueue(context);
// 创建图片缓存,使用应用可用内存的1/8作为缓存大小
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
mImageCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// 计算图片大小(KB)
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
// 创建ImageLoader
mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() {
@Override
public Bitmap getBitmap(String url) {
return mImageCache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
mImageCache.put(url, bitmap);
}
});
}
// 加载图片到ImageView
public void loadImage(String url, ImageView imageView) {
mImageLoader.get(url, ImageLoader.getImageListener(
imageView,
R.drawable.default_image, // 默认图片
R.drawable.error_image // 错误图片
));
}
// 加载图片并设置自定义尺寸
public void loadImage(String url, ImageView imageView, int maxWidth, int maxHeight) {
ImageLoader.ImageContainer container = mImageLoader.get(url,
ImageLoader.getImageListener(
imageView,
R.drawable.default_image,
R.drawable.error_image
),
maxWidth,
maxHeight
);
// 如果图片已经在缓存中,可以立即使用
Bitmap cachedBitmap = container.getBitmap();
if (cachedBitmap != null) {
imageView.setImageBitmap(cachedBitmap);
}
}
}
十五、RequestQueue与其他网络库的对比
15.1 Volley vs OkHttp
-
Volley优势:
- 内置请求队列和优先级管理
- 强大的缓存机制
- 响应自动分发到主线程
- 适合轻量级、高频的网络请求
-
OkHttp优势:
- 更底层的HTTP实现,性能更高
- 支持HTTP/2和WebSocket
- 丰富的拦截器机制
- 更好的连接池管理
- 适合复杂网络场景和大文件下载
15.2 Volley vs Retrofit
-
Volley优势:
- 无需预先定义API接口
- 更灵活的请求和响应处理
- 内置图片加载功能
- 适合快速开发和轻量级应用
-
Retrofit优势:
- 使用注解定义API,代码更简洁
- 与RxJava等响应式编程库集成良好
- 支持多种数据格式转换(JSON、XML等)
- 更适合大型项目和RESTful API调用
15.3 Volley vs HttpURLConnection/HttpClient
-
Volley优势:
- 高级的请求队列和调度机制
- 智能缓存策略,减少网络请求
- 自动处理请求优先级
- 内置JSON解析和图片加载
- 更友好的错误处理机制
-
HttpURLConnection/HttpClient优势:
- 更低的学习成本
- 更底层的控制能力
- 适合简单的网络请求场景
十六、RequestQueue的未来发展趋势
16.1 与Kotlin协程的集成
随着Kotlin成为Android开发的首选语言,Volley可能会加强与Kotlin协程的集成,提供更简洁的异步编程体验:
// 假设Volley未来支持协程的示例
suspend fun fetchData(url: String): Response<String> = suspendCoroutine { continuation ->
val request = object : StringRequest(Method.GET, url,
Response.Listener { response ->
continuation.resume(Response.success(response))
},
Response.ErrorListener { error ->
continuation.resumeWithException(error)
}
) {}
requestQueue.add(request)
}
16.2 对HTTP/3和QUIC的支持
随着HTTP/3和QUIC协议的普及,Volley可能会增加对这些新技术的支持,进一步提升网络请求性能:
// 假设未来Volley支持HTTP/3的示例
RequestQueue queue = Volley.newRequestQueue(context, new Http3Stack());
16.3 与Jetpack库的深度集成
Volley可能会与Jetpack库(如ViewModel、LiveData、Data Binding等)进行更深度的集成,提供更现代化的Android开发体验:
// 假设Volley与ViewModel集成的示例
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<String>()
val data: LiveData<String> = _data
init {
loadData()
}
private fun loadData() {
val request = StringRequest(
Request.Method.GET,
"https://api.example.com/data",
{ response ->
_data.value = response
},
{ error ->
// 处理错误
}
)
// 自动管理请求生命周期
RequestManager.getInstance().addRequest(this, request)
}
}
16.4 更智能的缓存和预加载机制
未来的Volley可能会引入更智能的缓存和预加载机制,基于用户行为和网络条件自动优化数据加载策略:
// 假设未来的智能缓存配置示例
CachePolicy policy = new CachePolicy.Builder()
.setExpirationTime(3600000) // 1小时
.setRefreshInterval(600000) // 10分钟
.setPreloadOnWiFi(true)
.setPreloadOnCellular(false)
.build();
request.setCachePolicy(policy);
16.5 增强的性能监控和调试工具
Volley可能会提供更强大的性能监控和调试工具,帮助开发者更轻松地识别和解决网络请求相关的问题:
// 假设未来的性能监控示例
RequestQueue queue = Volley.newRequestQueue(context);
PerformanceMonitor monitor = new PerformanceMonitor() {
@Override
public void onRequestStarted(Request<?> request) {
// 记录请求开始
}
@Override
public void onRequestCompleted(Request<?> request, long duration) {
// 记录请求完成和耗时
}
@Override
public void onRequestFailed(Request<?> request, VolleyError error) {
// 记录请求失败
}
};
queue.setPerformanceMonitor(monitor);
通过对Android Volley RequestQueue的深入分析,我们可以看到它是一个设计精良、功能强大的网络请求框架。虽然现在有许多新的网络库出现,但Volley仍然在许多场景下表现出色,特别是在轻量级应用和需要精细控制请求队列的场景中。理解RequestQueue的工作原理和内部机制,有助于我们更高效地使用Volley,优化应用的网络请求性能,提升用户体验。