volley尝试源码解析2: http://blog.youkuaiyun.com/charsion/article/details/52943183
接着上一篇,这边继续分析Volley类,先贴出余下的代码
{
...
RequestQueue queue;
if (maxDiskCacheBytes <= -1)
{
// No maximum size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}
else
{
// Disk cache size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
queue.start();
return queue;
}
- RequestQueue
- DiskBasedCache
分析完这两个类好像神秘的盒子就看的差不多了,Volley请求三部曲中第一部就算差不多分析完了,但是说真的就到现在我都不知道这剩下的两个类中有多少”鬼”在等着呢?现在就硬着头皮继续往下看,看到哪算哪吧.这样显得更真实嘛,以后精研的时候再返回来看这一系列的文章也能发觉自己看东西的陋习和错误的理解,废话不说了.直接进入主题.看到要生成RequestQueue实例,需要DiskBasedCache对象作为参数,所以首先分析DiskBasedCache
- DiskBasedCache
/**
* Cache implementation that caches files directly onto the hard disk in the specified
* directory. The default disk usage size is 5MB, but is configurable.
* 缓存实现类:将文件缓存到硬盘指定的目录下.默认的目录大小为5g,但是可以配置
*/
public class DiskBasedCache implements Cache { //以后就不大片大片的代码往上贴了,看的好多一下子就没有心情了
}
看类注释,终于发现了volley缓存机制的功能类了,实现了Cache 接口,进入Cache接口,发现接口除了有一个内部静态类Entry需要特别关注一下之外其它都没有什么东东,看看Entry是干嘛的
/**
* Data and metadata for an entry returned by the cache.
* 缓存返回的一个条目(做缓存总不能简简单单的存个int值吧,既然一堆东西总不能一个一个的存吧.毫无疑问想到的第一个反应就是把需要存的东西全部变成一个类的成员变量,自然Entry诞生了)
*/
public static class Entry {
....
}
知道了接口Cache 大概之后,我们接下来看看实现类DiskBasedCache.一看类的的行数不少啊,有点被吓到,但是从上到下快速浏览一遍之后,发现除了注释和import也没啥了,哟西,开始了
第一行(CacheHeader这是什么”鬼”,赶紧手抖一下点击类进去看看,发现还是在DiskBasedCache类中,原来又是一个内部类,瞬间开心起来)
/** Map of the Key, CacheHeader pairs */
private final Map<String, CacheHeader> mEntries =
new LinkedHashMap<String, CacheHeader>(16, .75f, true);
CacheHeader
/**
* Instantiates a new CacheHeader object
* @param key The key that identifies the cache entry
* @param entry The cache entry.
*/
public CacheHeader(String key, Entry entry) {
this.key = key; //缓存条目的key
this.size = entry.data.length; //缓存条目返回的缓存内容
this.etag = entry.etag; //标记缓存一致性
this.serverDate = entry.serverDate; //返回报文的日期
this.lastModified = entry.lastModified; //请求对象最后修改日期
this.ttl = entry.ttl; //ttl 记录
this.softTtl = entry.softTtl; //soft ttl记录
> ttl 是什么"鬼"我不懂,咋的(这是在跟代码较真的语气):http://baike.baidu.com/link?url=dM3MIiPBZhnfOMhs5jTV2MLZbVx7MXxJeHSglj85wBRiEPc76YMrGPmNprT9wwt5pLNxXm0DKlGqzFTCsx6lyq
this.responseHeaders = entry.responseHeaders;//Immutable response headers as received from server; must be non-null (返回报文头)
}
看完构造方法大概就知道这个内部累是干啥的了,这个内部类好,内容不难,但是一看一百多行,占了外部类的快一半了,哟西
- 继续DiskBasedCache类分析,来到了构造方法,没啥,过
- public synchronized void clear(){….} //清空指定目录下所有的文件,没啥,过
- public synchronized Entry get(String key) ;
/**
* Returns the cache entry with the specified key if it exists, null otherwise.
*/
@Override
public synchronized Entry get(String key) {
...
File file = getFileForKey(key);
CountingInputStream cis = null;
//先分析上面两行,再接着分析下面
try {
cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
CacheHeader.readHeader(cis); // eat header
byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
return entry.toCacheEntry(data);
} catch (IOException e) {
...
} catch (NegativeArraySizeException e) {
...
} finally {
...
}
...
}
看到方法中调用了getFileForKey(key)和多出了一个CountingInputStream类
getFileForKey(key)
/**
* Returns a file object for the given cache key. //根据key得到文件对象
*/
public File getFileForKey(String key) {
return new File(mRootDirectory, getFilenameForKey(key));
}
getFilenameForKey(key)
/**
* Creates a pseudo-unique filename for the specified cache key.
* @param key The key to generate a file name for.
* @return A pseudo-unique filename.
*/
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;
}
//耶,搞些啥玩意,发现就是根据key字符串做一些操作返回一个字符窜,没啥嘛,搞的这么悬乎.
经过上面的分析,知道调用了getFileForKey(key)得到一个文件对象(当然文件的路径不是瞎掰的,是有悬乎的,啥悬乎,上面不是有代码么,自己参透)
CountingInputStream
//原来是FilterInputStream子类啊
private static class CountingInputStream extends FilterInputStream {
.....
}
接着上面get(key)方法中没有分析的地方继续往下看
try { //通过流的一些操作,最终得到上面得到的缓存文件中缓存条目的信息并且返回了缓存条目对象
cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
CacheHeader.readHeader(cis); // eat header
byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
return entry.toCacheEntry(data);
} catch (IOException e) {
...
} catch (NegativeArraySizeException e) {
...
} finally {
...
}
接着分析DiskBasedCache类
- public synchronized void initialize(){ …}
/**
* Initializes the DiskBasedCache by scanning for all files currently in the
* specified root directory. Creates the root directory if necessary.
* 扫描指定目录下的所有文件,如果指定目录不存在就创建
*/
@Override
public synchronized void initialize() {
if (!mRootDirectory.exists()) {
if (!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) {
...
} finally {
...
}
}
}
public synchronized void invalidate(String key, boolean fullExpire){… } //修改entry的ttl和soft ttl值
public synchronized void put(String key, Entry entry) { … putEntry(String key, CacheHeader entry)… } //将指定的entry存入集合中,这个方法里有一个pruneIfNeeded(int length)方法,原理就是存入缓存之前首先判断即将存入的缓存数据的大小加上当前缓存中所有数据的大小是否超过缓存空间的总大小,如果大于就遍历缓存集合从头开始删除直到小于缓存空间总大小结束.在内部又调用了putEntry(String key, CacheHeader entry)方法.因为CacheHeader 才是真正作为缓存的存储单位对象.
- public synchronized void remove(String key) {….} //删除缓存文件和缓存单位对象集合
小结:上面分析完了DiskBasedCache,知道volley中缓存的大致流程,首先定义了一个接口Cache,定义了一些对缓存的增删改查的功能,它有一个内部类Entry,声明了需要缓存的一个个内容( byte[] data , Map
RequestQueue
还是分两次吧,下一篇接着这篇继续