/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.volley;
import android.os.Process;
import java.util.concurrent.BlockingQueue;
/**
* Provides a thread for performing cache triage on a queue of requests.
*
* Requests added to the specified cache queue are resolved from cache.
* Any deliverable response is posted back to the caller via a
* {@link ResponseDelivery}. Cache misses and responses that require
* refresh are enqueued on the specified network queue for processing
* by a {@link NetworkDispatcher}.
*/
/*
* 从 缓存 Request 队列中取出 缓存 Request
* 然后 根据 缓存 Request CacheKey 去硬盘缓存( DiskBasedCache )映射过来的内存缓存中寻找 是否存在 Entry ( Response )
* 1. 存在的话,通过 DefaultRetryPolicy 回传相关数据
* 2. 存在但缓存需要刷新的话,放入 网络 Request 队列内,会在 NetworkDispatcher 的循环体中被用来重新请求
* 3. 不存在的话,将该 Request,放入 网络 Request 队列内,会在 NetworkDispatcher 的循环体中被用来重新请求
*/
public class CacheDispatcher extends Thread {
// 记录 VolleyLog 的 debug 开关
private static final boolean DEBUG = VolleyLog.DEBUG;
/** The queue of requests coming in for triage. */
/*
* 保存 缓存 Request,因为这里会涉及到 并发
* 所以,采用 BlockingQueue
*/
private final BlockingQueue<Request<?>> mCacheQueue;
/** The queue of requests going out to the network. */
/*
* 保存 网络 Request,因为这里会涉及到 并发
* 所以,采用 BlockingQueue
*/
private final BlockingQueue<Request<?>> mNetworkQueue;
/** The cache to read from. */
/*
* 当前 CacheDispatcher 线程 读取 缓存阻塞队列 mCacheQueue 一个 缓存 Request<?> 后
* 会获取其 的 的 缓存 key = CacheKey
* 这里的 Cache 其实是一个 DiskBasedCache 缓存
* 根据缓存 key 从 DiskBasedCache 缓存中获取一个请求
*/
private final Cache mCache;
/** For posting responses. */
// 用于 传递 缓存 Request 中 抽取出 Response
private final ResponseDelivery mDelivery;
/** Used for telling us to die. */
// 结束标记,标记这个 CacheDispatcher 线程是否结束
private volatile boolean mQuit = false;
/**
* Creates a new cache triage dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*
* @param cacheQueue Queue of incoming requests for triage
* @param networkQueue Queue to post requests that require network to
* @param cache Cache interface to use for resolution
* @param delivery Delivery interface to use for posting responses
*/
/*
* cacheQueue:缓存 Request<?> 队列,RequestQueue 中传入进来的
* networkQueue:网络 Request<?> 队列,RequestQueue 中传入进来的
* cache:DiskBasedCache 对象,用于读取硬盘上的缓存数据。Volley -> RequestQueue -> this
* delivery:ExecutorDelivery 对象,RequestQueue 中传入进来的
*/
public CacheDispatcher(BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue, Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}
/**
* Forces this dispatcher to quit immediately. If any requests are still in
* the queue, they are not guaranteed to be processed.
*/
/*
* 线程结束
*/
public void quit() {
// 设置 结束标记
mQuit = true;
// 线程中断,run() 内会抛出一个 InterruptedException
interrupt();
}
@Override public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
// 设置 该线程优先级为 THREAD_PRIORITY_BACKGROUND
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
/*
* 执行 DiskBasedCache 的初始化操作:
* 1. 判断缓存目录是否存在,不存在则创建一系列文件夹,然后返回
* 2. 存在缓存文件,开始读取缓存文件内容。每一个缓存文件内容对应一个 CacheHeader
*/
mCache.initialize();
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
// 从 缓存 Request 队列内,拿出一个 Request
final Request<?> request = mCacheQueue.take();
// 为请求添加一个 "cache-queue-take" MarkLog
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
// 如果 Request 已经被取消了
if (request.isCanceled()) {
// 关闭请求,打印 请求中的 MarkLog
request.finish("cache-discard-canceled");
// 跳过此次,然后继续循环
continue;
}
// Attempt to retrieve this item from cache.
/*
* 从 缓存 Request 中,获取缓存 key
* 通过缓存 key,去硬盘缓存中,获取对应的 Response( Entry )数据
*/
Cache.Entry entry = mCache.get(request.getCacheKey());
// 如果缓存 Entry 不存在
if (entry == null) {
// 为请求添加一个 "cache-miss" MarkLog
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
/*
* 由于 缓存 Response 丢失,所以重新放去 网络 Request 队列内
* 重新请求
*/
mNetworkQueue.put(request);
// 跳过此次,然后继续循环
continue;
}
// If it is completely expired, just send it to the network.
// 如果缓存 Entry 到了过期时间
if (entry.isExpired()) {
// 为请求添加一个 "cache-hit-expired" MarkLog
request.addMarker("cache-hit-expired");
// 为 请求 添加 缓存的 Response 数据( Entry )
request.setCacheEntry(entry);
/*
* 由于 缓存 Response 过期,所以重新放去 网络 Request 队列内
* 重新请求
*/
mNetworkQueue.put(request);
// 跳过此次,然后继续循环
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
/*
* 为请求添加一个 "cache-hit" MarkLog
* 说明 硬盘缓存 中存在 该缓存 Request 对应的 Response 数据 ( Entry )
*/
request.addMarker("cache-hit");
/*
* 开始 拿到 硬盘缓存 中的 Response 数据( Entry )
* 去解析该 Entry 数据,转换成 Volley 定义的 Response
*/
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
// 为请求添加一个 "cache-hit-parsed" MarkLog
request.addMarker("cache-hit-parsed");
// 判断 缓存 Entry 是否需要刷新
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
// 不需要刷新,则直接回传 Request 和 Response 数据
mDelivery.postResponse(request, response);
} else {
// Soft-expired cache hit. We can deliver the cached response,
// but we need to also send the request to the network for
// refreshing.
/*********************
* 缓存 Entry 需要刷新 *
*********************/
// 为请求添加一个 "cache-hit-refresh-needed" MarkLog
request.addMarker("cache-hit-refresh-needed");
// 为 请求 添加 缓存的 Response 数据( Entry )
request.setCacheEntry(entry);
// Mark the response as intermediate.
/*
* 为了打印一个 intermediate-response 的 MarkLog
*/
response.intermediate = true;
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
/*
* 由于 缓存 Entry 需要刷新
* 所以在 回传 Request 和 Response 数据 的时候
* 执行一个 Runnable 去将该请求
* 添加到 网络 Request 队列内
*/
mDelivery.postResponse(request, response, new Runnable() {
@Override public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
/****************************
* 调用了 Thread.interrupt() *
****************************/
// 查看 结束标记 是否为 true
if (mQuit) {
// 退出 循环体
return;
}
// 结束标记 为 false,跳过此次,然后继续循环
continue;
}
}
}
}