标签:
CacheDispatcher
缓存的具体执行类,继承ThreadDiskBasedCache
缓存核心类,基于Disk的缓存实现类Cache.Entry
真正HTTP请求的缓存实体DiskBasedCache.CacheHeader
和Cache.Entry
一样,就是不存储响应体,只存储了缓存的大小DiskBasedCache.CountingInputStream
添加了记录字节功能的流,继承FilterInputStream /** 可以走Disk缓存的request请求队列. */
private final BlockingQueue<Request<?>> mCacheQueue;
/** 需要走网络的request请求队列. */
private final BlockingQueue<Request<?>> mNetworkQueue;
/** DiskBasedCache缓存实现类. */
private final Cache mCache;
/** 网络请求结果传递类. */
private final ResponseDelivery mDelivery;
/** 用来停止线程的标志位. */
private volatile boolean mQuit = false;
@Override
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 初始化DiskBasedCache缓存类.
mCache.initialize();
while (true) {
try {
// 从缓存队列中获取request请求.(缓存队列实现了生产者-消费者队列模型)
final Request<?> request = mCacheQueue.take();
// 判断请求是否被取消
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 从缓存系统中获取request请求结果Cache.Entry.
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
// 如果缓存系统中没有该缓存请求,则将request加入到网络请求队列中.
// 由于NetworkQueue跟NetworkDispatcher线程关联,并且也是生产者-消费者队列,
// 所以这里添加request请求就相当于将request执行网络请求.
mNetworkQueue.put(request);
continue;
}
// 判断缓存结果是否过期.
if (entry.isExpired()) {
request.setCacheEntry(entry);
// 过期的缓存需要重新执行request请求.
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data,
entry.responseHeaders));
// 判断Request请求结果是否新鲜?
if (!entry.refreshNeeded()) {
// 请求结果新鲜,则直接将请求结果分发,进行异步回调用户接口.
mDelivery.postResponse(request, response);
} else {
// 请求结果不新鲜,但是同样还是将缓存结果返回给用户,并且同时执行网络请求,刷新Request网络结果缓存.
request.setCacheEntry(entry);
response.intermediate = true;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
if (mQuit) {
return;
}
}
}
}
class Entry {
/** HTTP响应体. */
public byte[] data;
/** HTTP响应首部中用于缓存新鲜度验证的ETag. */
public String etag
/** HTTP响应时间. */
public long serverDate;
/** 缓存内容最后一次修改的时间. */
public long lastModified;
/** Request的缓存过期时间. */
public long ttl;
/** Request的缓存新鲜时间. */
public long softTtl;
/** HTTP响应Headers. */
public Map<String, String> responseHeaders = Collections.emptyMap();
/** 判断缓存内容是否过期. */
public boolean isExpired() {
return this.ttl < System.currentTimeMillis();
}
/** 判断缓存是否新鲜,不新鲜的缓存需要发到服务端做新鲜度的检测. */
public boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}
}
DiskBasedCache.CacheHeader
和它的不同在于它不存储响应体,只存储了缓存的大小 @Override
public int read() throws IOException {
int result = super.read();
if (result != -1) {
bytesRead ++;
}
return result;
}
private static void writeInt(OutputStream os, int n) throws IOException {
os.write((n) & 0xff);
os.write((n >> 8) & 0xff);
os.write((n >> 16) & 0xff);
os.write((n >> 24) & 0xff);
}
/** 默认硬盘最大的缓存空间(5M). */
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
/** 标记缓存起始的MAGIC_NUMBER. */
private static final int CACHE_MAGIC = 0x20150306;
/**
* High water mark percentage for the cache.
*/
private static final float HYSTERESIS_FACTOR = 0.9f;
/**
* Map of the Key, CacheHeaders pairs.
* accessOrder为true很关键
*/
private final Map<String, CacheHeader> mEntries =
new LinkedHashMap<String, CacheHeader>(16, 0.75f, true);
/** 目前使用的缓存字节数. */
private long mTotalSize = 0;
/** 硬盘缓存目录. */
private final File mRootDirectory;
/** 硬盘缓存最大容量(默认5M). */
private final int mMaxCacheSizeInBytes;
private final Map<String, CacheHeader> mEntries =
new LinkedHashMap<String, CacheHeader>(16, 0.75f, true);
initialize()
函数的作用是遍历Disk缓存系统,将缓存文件读出来分为key:url和value:CacheHeader存入到Map中 @Override
public void initialize() {
if (!mRootDirectory.exists() && !mRootDirectory.mkdirs()) {
return;
}
File[] files = mRootDirectory.listFiles();
if (files == null) {
return;
}
for (File file : files) {
BufferedInputStream fis = null;
try {
fis = new BufferedInputStream(new FileInputStream(file));
CacheHeader entry = CacheHeader.readHeader(fis);
entry.size = file.length();
putEntry(entry.key, entry);
}catch (IOException e) {
file.delete();
e.printStackTrace();
}finally {
if (fis != null) {
try {
fis.close();
} catch (IOException ignored) {
}
}
}
}
}
CacheHeader entry = CacheHeader.readHeader(fis);
这个函数的作用就是按照写入的顺序把相应的数据读出来,其实就是对象的反序列化 public static CacheHeader readHeader(InputStream is) throws IOException {
CacheHeader entry = new CacheHeader();
// 以CACHE_NUMBER作为读取一个对象的开始
int magic = readInt(is);
if (magic != CACHE_MAGIC) {
throw new IOException();
}
entry.key = readString(is);
entry.etag = readString(is);
if (entry.etag.equals("")) {
entry.etag = null;
}
entry.serverDate = readLong(is);
entry.lastModified = readLong(is);
entry.ttl = readLong(is);
entry.softTtl = readLong(is);
entry.responseHeaders = readStringStringMap(is);
return entry;
putEntry(entry.key, entry);
这个函数,将key和value存储到内存中,并更新总字节数(判断缓存是否满) private void putEntry(String key, CacheHeader entry) {
if (!mEntries.containsKey(key)) {
mTotalSize += entry.size;
} else {
CacheHeader oldEntry = mEntries.get(key);
mTotalSize += (entry.size - oldEntry.size);
}
mEntries.put(key, entry);
}
/** Disk缓存替换更新机制. */
private void pruneIfNeeded(int neededSpace) {
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) {
return;
}
Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, CacheHeader> entry = iterator.next();
CacheHeader e = entry.getValue();
boolean deleted = getFileForKey(e.key).delete();
if (deleted) {
mTotalSize -= e.size;
}
iterator.remove();
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {
break;
}
}
}
想一想为什么要有一个看无用的DiskBasedCache.CacheHeader类,因为为了避免在内存中存储过多的东西,用的时候在临时构造(从文件中拿响应体的内容)
public synchronized Entry get(String key) {
CacheHeader entry = mEntries.get(key);
if (entry == null) {
return null;
}
File file = getFileForKey(key);
CountingInputStream cis = null;
try {
cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
// 读完CacheHeader部分,并通过CountingInputStream的bytesRead成员记录已经读取的字节数.
CacheHeader.readHeader(cis);
// 读取缓存文件存储的HTTP响应体内容.
byte[] data = streamToBytes(cis, (int)(file.length() - cis.bytesRead));
return entry.toCacheEntry(data);
} catch (IOException e) {
remove(key);
return null;
} finally {
if (cis != null) {
try {
cis.close();
} catch (IOException ignored) {
}
}
}
}
@Override
public synchronized void put(String key, Entry entry) {
pruneIfNeeded(entry.data.length);
File file = getFileForKey(key);
try {
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
//构造Header
CacheHeader e = new CacheHeader(key, entry);
//向文件中写入除了响应体之外的内容
boolean success = e.writeHeader(fos);
if (!success) {
fos.close();
throw new IOException();
}
//把响应体的内容写到文件中
fos.write(entry.data);
fos.close();
//把数据存储到内存中
putEntry(key, e);
return;
} catch (IOException e) {
e.printStackTrace();
}
//如果出现异常,就把文件删除
file.delete();
}
/** 清空缓存内容. */
@Override
public synchronized void clear() {
File[] files = mRootDirectory.listFiles();
if (files != null) {
for (File file : files) {
file.delete();
}
}
mEntries.clear();
mTotalSize = 0;
}
/** 标记指定的cache过期. */
@Override
public synchronized void invalidate(String key, boolean fullExpire) {
Entry entry = get(key);
if (entry != null) {
entry.softTtl = 0;
if (fullExpire) {
entry.ttl = 0;
}
put(key, entry);
}
}
/** 获取存储当前key对应value的文件句柄. */
private File getFileForKey(String key) {
return new File(mRootDirectory, getFilenameForKey(key));
}
/** 根据key的hash值生成对应的存储文件名称. */
private String getFilenameForKey(String key) {
int firstHalfLength = key.length() / 2;
String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
return localFilename;
}
@Override
public synchronized void remove(String key) {
boolean deleted = getFileForKey(key).delete();
removeEntry(key);
if (!deleted) {
Log.e("Volley", "没能删除key=" + key + ", 文件名=" + getFilenameForKey(key) + "缓存.");
}
}
/** 从Map对象中删除key对应的键值对. */
private void removeEntry(String key) {
CacheHeader entry = mEntries.get(key);
if (entry != null) {
mTotalSize -= entry.size;
mEntries.remove(key);
}
}
public long ttl
,在删除缓存的时候pruneIfNeeded
直接从队列前端删除真的好吗?有没有更好的方法?当然有,鉴于缓存的过期时间,我们可以以这个为基点,遍历一遍,优先删除快过期的缓存,或者我们存储时就按缓存过期时间存储,这样可能会让Volley有更好的表现 /** 根据key的hash值生成对应的存储文件名称. */
private String getFilenameForKey(String key) {
int firstHalfLength = key.length() / 2;
String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
return localFilename;
}
标签:
原文地址:http://blog.csdn.net/geekerhw/article/details/51981311