通过Volley我们能学到什么?(3) — 缓存原理
2016-01-29 17:11
435 查看
目录
构建缓存容器
生成缓存
向容器中插入缓存
获取缓存
构建缓存容器
在最初创建Volley的RequestQueue对象时,构造DiskBasedCache类时如果不设置缓存大小的话,默认缓存是5M;
//默认缓存5M
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
或
//自定义缓存xM
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir,x*1024*1024), network);
生成缓存
生成缓存大体逻辑是在通过网络成功获取数据后,在解析成Response对象的过程中将获取的内容拷贝到缓存对象中。
NetworkDispatcher.java
这里只看第5行的parseNetworkResponse()。
parseNetworkResponse()方法里面最终默认调用的是
而生成缓存的方法就在parseCacheHeaders(response)方法里面,所以我们主要看它。
HttpHeaderParser.java
1、获取"Date”字段,即服务器时间(7 - 10行);
2、获取"Cache-Control”信息,即缓存过期时间(12 - 15行);
3、如果不允许缓存,直接返回null(18 - 19行);
4、“max-age=”为缓存过期时间(20 - 24行);
5、“stale-while-revalidate=”为过了缓存时间后还可以继续使用缓存的时间,即真正的缓存时间是“max-age=” + “stale-while-revalidate=”的总时间,但如果有“must-revalidate”或者“proxy-revalidate”字段则过了缓存时间缓存就立即请求服务器(25 - 32行);
6、“Expires”字段是一个时间点,其实和Cache-Control的意思是一致的,控制缓存的过期时间的,是为了兼容HTTP1.0和HTTP1.1的才会使用的字段,如果有Cache-Control的话优先使用Cache-Control(36 - 39行);
7、“Last-Modified”字段是服务器最后修改时间,如果缓存过期了,带上“Last-Modified”这个字段的时间与服务器的最后修改时间比较,如果服务器最后修改时间比“Last-Modified”字段的时间新,则重新获取,如果交旧,则返回304,继续用之前的缓存(41 - 44行);
8、“ETag”字段是服务器资源的唯一标识符,与“Last-Modified”配合使用,因为“Last-Modified”只能精确到秒。如果“ETag”与服务器一致,再判断“Last-Modified”,防止一秒内服务器多次修改而导致数据不准确的情况(46行);
9、如果支持Cache-Control则使用Cache-Control中的缓存验证信息,否则使用“Expires”中的验证信息(50 - 59行);
10、将服务器请求内容和验证信息都赋值给entry对象(61 - 68行);
缓存对象创建完成。
向容器中插入缓存
插入缓存还是看NetworkDispatcher.java中的run()里面。
第11行的put()方法为缓存的插入,向下看源码。
DiskBasedCache.java
1、判断要插入新的缓存文件后会不会超出缓存最大值(2行);
①如果当前已缓存总大小 + 要插入缓存的大小小于缓存最大值直接返回,否则需要清理(247 - 249行);
②获取所有缓存头的集合,逐一删除集合中key值对应的文件夹,也就是删除其对应的缓存文件,直到当前缓存空间可以容纳要插入的缓存为止(259 - 275行);
2、创建缓存文件(3行);
3、将头部信息和数据内容写入到缓存文件里面(5 - 14行);
4、将头文件放入临时存储所有头部信息的容器中,更新并计算所有缓存头文件的大小和当前对应缓存头文件的内容(15行);
5、如果出现IO异常删掉创建的文件;
这样缓存就插入到缓存文件里面了,其中涉及一些IO知识。
获取缓存
1、预先根据URL组成的Key在存储缓存头的容器中来搜索缓存头文件信息,这样就省去文件搜索,更快捷(2 - 6行);
2、找到存储缓存的文件夹(8行);
3、将缓存内容读出来并返回(9 - 14行);
总结:
这个版本的Volley只是一级的缓存,未存储在磁盘中。
构建缓存容器
生成缓存
向容器中插入缓存
获取缓存
构建缓存容器
在最初创建Volley的RequestQueue对象时,构造DiskBasedCache类时如果不设置缓存大小的话,默认缓存是5M;
//默认缓存5M
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
或
//自定义缓存xM
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir,x*1024*1024), network);
生成缓存
生成缓存大体逻辑是在通过网络成功获取数据后,在解析成Response对象的过程中将获取的内容拷贝到缓存对象中。
NetworkDispatcher.java
public void run() { ...... Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); // Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for 304s. if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } ...... }
这里只看第5行的parseNetworkResponse()。
parseNetworkResponse()方法里面最终默认调用的是
Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
而生成缓存的方法就在parseCacheHeaders(response)方法里面,所以我们主要看它。
HttpHeaderParser.java
public static Cache.Entry parseCacheHeaders(NetworkResponse response) { ……. String serverEtag = null; String headerValue; headerValue = headers.get("Date"); if (headerValue != null) { serverDate = parseDateAsEpoch(headerValue); } headerValue = headers.get("Cache-Control"); if (headerValue != null) { hasCacheControl = true; String[] tokens = headerValue.split(","); for (int i = 0; i < tokens.length; i++) { String token = tokens[i].trim(); if (token.equals("no-cache") || token.equals("no-store")) { return null; } else if (token.startsWith("max-age=")) { try { maxAge = Long.parseLong(token.substring(8)); } catch (Exception e) { } } else if (token.startsWith("stale-while-revalidate=")) { try { staleWhileRevalidate = Long.parseLong(token.substring(23)); } catch (Exception e) { } } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) { mustRevalidate = true; } } } headerValue = headers.get("Expires"); if (headerValue != null) { serverExpires = parseDateAsEpoch(headerValue); } headerValue = headers.get("Last-Modified"); if (headerValue != null) { lastModified = parseDateAsEpoch(headerValue); } serverEtag = headers.get("ETag"); // Cache-Control takes precedence over an Expires header, even if both exist and Expires // is more restrictive. if (hasCacheControl) { softExpire = now + maxAge * 1000; finalExpire = mustRevalidate ? softExpire : softExpire + staleWhileRevalidate * 1000; } else if (serverDate > 0 && serverExpires >= serverDate) { // Default semantic for Expire header in HTTP specification is softExpire. softExpire = now + (serverExpires - serverDate); finalExpire = softExpire; } Cache.Entry entry = new Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; entry.ttl = finalExpire; entry.serverDate = serverDate; entry.lastModified = lastModified; entry.responseHeaders = headers; return entry; }
1、获取"Date”字段,即服务器时间(7 - 10行);
2、获取"Cache-Control”信息,即缓存过期时间(12 - 15行);
3、如果不允许缓存,直接返回null(18 - 19行);
4、“max-age=”为缓存过期时间(20 - 24行);
5、“stale-while-revalidate=”为过了缓存时间后还可以继续使用缓存的时间,即真正的缓存时间是“max-age=” + “stale-while-revalidate=”的总时间,但如果有“must-revalidate”或者“proxy-revalidate”字段则过了缓存时间缓存就立即请求服务器(25 - 32行);
6、“Expires”字段是一个时间点,其实和Cache-Control的意思是一致的,控制缓存的过期时间的,是为了兼容HTTP1.0和HTTP1.1的才会使用的字段,如果有Cache-Control的话优先使用Cache-Control(36 - 39行);
7、“Last-Modified”字段是服务器最后修改时间,如果缓存过期了,带上“Last-Modified”这个字段的时间与服务器的最后修改时间比较,如果服务器最后修改时间比“Last-Modified”字段的时间新,则重新获取,如果交旧,则返回304,继续用之前的缓存(41 - 44行);
8、“ETag”字段是服务器资源的唯一标识符,与“Last-Modified”配合使用,因为“Last-Modified”只能精确到秒。如果“ETag”与服务器一致,再判断“Last-Modified”,防止一秒内服务器多次修改而导致数据不准确的情况(46行);
9、如果支持Cache-Control则使用Cache-Control中的缓存验证信息,否则使用“Expires”中的验证信息(50 - 59行);
10、将服务器请求内容和验证信息都赋值给entry对象(61 - 68行);
缓存对象创建完成。
向容器中插入缓存
插入缓存还是看NetworkDispatcher.java中的run()里面。
public void run() { ...... Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); // Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for 304s. if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } ...... }
第11行的put()方法为缓存的插入,向下看源码。
DiskBasedCache.java
public synchronized void put(String key, Entry entry) { pruneIfNeeded(entry.data.length); File file = getFileForKey(key); try { BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file)); CacheHeader e = new CacheHeader(key, entry); boolean success = e.writeHeader(fos); if (!success) { fos.close(); VolleyLog.d("Failed to write header for %s", file.getAbsolutePath()); throw new IOException(); } fos.write(entry.data); fos.close(); putEntry(key, e); return; } catch (IOException e) { } boolean deleted = file.delete(); if (!deleted) { VolleyLog.d("Could not clean up file %s", file.getAbsolutePath()); } }
1、判断要插入新的缓存文件后会不会超出缓存最大值(2行);
①如果当前已缓存总大小 + 要插入缓存的大小小于缓存最大值直接返回,否则需要清理(247 - 249行);
②获取所有缓存头的集合,逐一删除集合中key值对应的文件夹,也就是删除其对应的缓存文件,直到当前缓存空间可以容纳要插入的缓存为止(259 - 275行);
2、创建缓存文件(3行);
3、将头部信息和数据内容写入到缓存文件里面(5 - 14行);
4、将头文件放入临时存储所有头部信息的容器中,更新并计算所有缓存头文件的大小和当前对应缓存头文件的内容(15行);
5、如果出现IO异常删掉创建的文件;
这样缓存就插入到缓存文件里面了,其中涉及一些IO知识。
获取缓存
public synchronized Entry get(String key) { CacheHeader entry = mEntries.get(key); // if the entry does not exist, return. if (entry == null) { return null; } 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) { VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString()); remove(key); return null; } finally { if (cis != null) { try { cis.close(); } catch (IOException ioe) { return null; } } } }
1、预先根据URL组成的Key在存储缓存头的容器中来搜索缓存头文件信息,这样就省去文件搜索,更快捷(2 - 6行);
2、找到存储缓存的文件夹(8行);
3、将缓存内容读出来并返回(9 - 14行);
总结:
这个版本的Volley只是一级的缓存,未存储在磁盘中。
相关文章推荐
- python 初学正则表达式
- HELLO WORLD
- UITableView的性能优化
- ios全景图片浏览App
- jquery.datatable.js与CI整合 异步加载(大数据量处理)
- 哈希查找
- 如何完全卸载oracle11g?
- MYSQL 配置slave故障
- Android JNI 第一个例子
- java生成图片验证码
- linux命令-more|less
- Matplotlib 教程
- http://blog.csdn.net/majian_1987/article/details/44939911
- mysql 配置主从
- 例题 5-1 STL
- Android多国语言的value文件夹命名方式
- 有关java编辑PDF的一些小问题
- Swift 基本知识之十三 构造函数
- [攻城掠池]
- Hdu 2602 Bone Collector