Volley源码分析(二)CacheDispatcher分析
CacheDispatcher 缓存分发
cacheQueue只是一个优先队列,我们在start方法中,分析了CacheDispatcher的构成是需要cacheQueue,然后调用CacheDispatcher.start方法,我们看一下CacheDispatcher得到cacheQueue之后,到底做了什么。
CacheQueue是一个继承于Thread的类,其start方法实质上是调用了run方法,我们看一下run方法所做的事情
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
mCache.initialize();
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
}
// If it is completely expired, just send it to the network.
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the 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.
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
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.
if (mQuit) {
return;
}
}
}
}
我们可以看出其Run方法是一个无限循环的方法,退出的方式只有产生中断异常,也就是其thread对象调用了 interrupt()方法,这个方法是在requestQueue中的stop方法中调用了,上面我们已经分析了。
下面我们主要run方法的执行过程,取出队头的request,然后判断request是否被取消,如果没有就判断该request中取出entity,判断entity的状态,如果entity为空,则将该request放入NetWorkDispatcher中重新请求,如果entity过期了也将该request放入NetWorkDispatcher中重新请求。两者都没有,则从request中entity的内容,重新构造response,然后判断该entity是否需要刷新,不需要就直接Delivery该response,如果需要刷新,则将该response依旧发给用户,但是重新进行请求该刷新entity。
可以用下图的逻辑去看上面的过程
这里,涉及到了Http一个重要的点,缓存。我们看一下,entity中关于缓存是怎么设置的.
class Entry {
/** The data returned from cache. */
public byte[] data;
/** ETag for cache coherency. */
public String etag;
/** Date of this response as reported by the server. */
public long serverDate;
/** The last modified date for the requested object. */
public long lastModified;
/** TTL for this record. */
public long ttl;
/** Soft TTL for this record. */
public long softTtl;
/** Immutable response headers as received from server; must be non-null. */
public Map<String, String> responseHeaders = Collections.emptyMap();
/** True if the entry is expired. */
boolean isExpired() {
return this.ttl < System.currentTimeMillis();
}
/** True if a refresh is needed from the original data source. */
boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}
}
这里的过期方法判断与是否需要刷新都是通过TTL与softTTL和现在时间对比而得到的。
缓存
这里说一下Volley的缓存机制,涉及到Http缓存,需要解析Http响应报文的头部。
public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
long now = System.currentTimeMillis();
Map<String, String> headers = response.headers;
long serverDate = 0;
long lastModified = 0;
long serverExpires = 0;
long softExpire = 0;
long finalExpire = 0;
long maxAge = 0;
long staleWhileRevalidate = 0;
boolean hasCacheControl = false;
boolean mustRevalidate = false;
String serverEtag;
String headerValue;
headerValue = headers.get("Date");
if (headerValue != null) {
serverDate = parseDateAsEpoch(headerValue);
}
// 获取响应体的Cache缓存策略.
headerValue = headers.get("Cache-Control");
if (headerValue != null) {
hasCacheControl = true;
String[] tokens = headerValue.split(",");
for (String token : tokens) {
token = token.trim();
if (token.equals("no-cache") || token.equals("no-store")) {
// no-cache|no-store代表服务器禁止客户端缓存,每次需要重新发送HTTP请求
return null;
} else if (token.startsWith("max-age=")) {
// 获取缓存的有效时间
try {
maxAge = Long.parseLong(token.substring(8));
} catch (Exception e) {
maxAge = 0;
}
} else if (token.startsWith("stale-while-revalidate=")) {
try {
staleWhileRevalidate = Long.parseLong(token.substring(23));
} catch (Exception e) {
staleWhileRevalidate = 0;
}
} 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");
// 计算缓存的ttl和softTtl
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;
}
这里设计到缓存,就要先得到Http的cache-control的headervalue,如果是no-cahce||no-store就不需要再处理缓存,虽然on-cache在浏览器那边还是保存了请求的资源,但这里去没有处理。如果headervalue中有MaxAge,这个值是判断缓存存在的有效时间。如果headervalue中有stale-while-revalidate,这个值是缓存过期的可用时间,即使缓存过期,在stale-while-revalidate时间内依旧可用。如果headervalue中有must-revalidate就意味着
从必须再验证缓存的新鲜度,然后再用。
然后继续解析header与缓存有关的内容,如Expires(这是一个不推荐的标签),Last-Modified(最近被修改的时间),ETag(服务器资源标识)。
然后如果有缓存控制就计算缓存的TTL与SoftTTL,SoftTTL就是softExpire,其值就是maxAge + 当前时间,而TTL是finalTTL其值是 先判断是否过期就再验证,如果是的话,其值就是softExpire,如果不是的话,其值就是softExpire加上staleWhileRevalidate(缓存过期有效时间)。
如果没有缓存控制,softExpire = now + (serverExpires - serverDate);
所以,说回上面,CacheQueue中缓存的判断,isExpire就是判断finalTTL是否超过当前时间,而refreshNeeded则是判断softExpire是否超过当前时间。
Volley源码分析(二)CacheDispatcher分析的更多相关文章
- # Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析#
Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析 Volley源码一共40多个类和接口.除去一些工具类的实现,核心代码只有20多个类.所以相对来说分析起来没有那么吃力.但是要想分析透 ...
- Volley源码分析(2)----ImageLoader
一:imageLoader 先来看看如何使用imageloader: public void showImg(View view){ ImageView imageView = (ImageView) ...
- Android Volley源码分析
今天来顺手分析一下谷歌的volley http通信框架.首先从github上 下载volley的源码, 然后新建你自己的工程以后 选择import module 然后选择volley. 最后还需要更改 ...
- Volley源码分析一
Volley源码分析 虽然在2017年,volley已经是一个逐渐被淘汰的框架,但其代码短小精悍,网络架构设计巧妙,还是有很多值得学习的地方. 第一篇文章,分析了请求队列的代码,请求队列也是我们使用V ...
- Volley源码分析(一)RequestQueue分析
Volley源码分析 虽然在2017年,volley已经是一个逐渐被淘汰的框架,但其代码短小精悍,网络架构设计巧妙,还是有很多值得学习的地方. 第一篇文章,分析了请求队列的代码,请求队列也是我们使用V ...
- Volley 源码分析
Volley 源码分析 图片分析 要说源码分析,我们得先看一下官方的配图: 从这张图中我们可以了解到 volley 工作流程: 1.请求加入优先队列 2.从缓存调度器中查看是否存在该请求,如果有(没有 ...
- Volley源码解析(三) 有缓存机制的情况走缓存请求的源码分析
Volley源码解析(三) 有缓存机制的情况走缓存请求的源码分析 Volley之所以高效好用,一个在于请求重试策略,一个就在于请求结果缓存. 通过上一篇文章http://www.cnblogs.com ...
- HashMap的源码学习以及性能分析
HashMap的源码学习以及性能分析 一).Map接口的实现类 HashTable.HashMap.LinkedHashMap.TreeMap 二).HashMap和HashTable的区别 1).H ...
- 物联网防火墙himqtt源码之MQTT协议分析
物联网防火墙himqtt源码之MQTT协议分析 himqtt是首款完整源码的高性能MQTT物联网防火墙 - MQTT Application FireWall,C语言编写,采用epoll模式支持数十万 ...
随机推荐
- 乱糟unity整理
当Canvas上的UI元素变化时,会重新生成网格并向GPU发起绘图调用,从而显示UI.划分画布:1.每块画布上的元素都与其他画布的元素相隔离,使用?工具来切分画布?,从而解决ui的批处理问题.2.也可 ...
- 常见的NoSQL数据库
NoSQL数据库发展迅猛,据说现在已经有上百种NoSQL数据库了,下面来了解下常见的一些NoSQL数据库 先来看张表,了解下典型的NoSQL数据库的分类 临时性键值存储 永久性键值存储 面向文档的数据 ...
- Java代码优化笔记
指定类.方法的final修饰符 为类指定final修饰符可以让类不可以被继承,为方法指定final修饰符可以让方法不可以被重写.如果指定了一个类为final,则该类所有的方法都是final的.Java ...
- sublime3 常用快捷键
轻量级编辑器,一直用的sublime text3 , 可以根据自己喜好安装喜欢的风格插件,根据工作需求安装代码处理插件. 下一章将推荐我常用的一些风格与代码插件 这里记录一些sublime 常用的快捷 ...
- tinyint、smallint、bigint、int 区别
1byte=8bit [tinyint] 从 0 到 255 的整型数据.存储大小为 1 字节.如果设置为UNSIGNED类型,只能存储从0到255的整数,不能用来储存负数. [smallint] ...
- 敏捷开发的道与术---MPD软件工作坊培训感想(上)
注:由麦思博(MSUP)主办的2013年亚太软件研发团队管理峰会(以下简称MPD大会)分别于6月15及6月22日在北京.上海举办,葡萄城的部分程序员参加了上海的会议,本文是参会的一些感受和心得. 这次 ...
- RaPC栅格化多边形裁剪之——进化0.1
采用整数二维数组进行cell的归属标记,将所有符合条件的cell输出,不进行整体多边形重构,用以统计面积. 上图: INTERSECT: 网格区域为离散化的空间范围,黄色部分为求交结果. differ ...
- VMware Linux虚拟机与WIN7操作系统共享无线网络上网配置
Linux虚拟机与WIN7操作系统共享无线网络上网配置 by:授客 QQ:1033553122 测试环境: CentOS-7-x86_64-DVD-1503-01.iso Vmware 9 实践操作: ...
- js替换数组中字符串实例
这个是替换数组中的一个对象字符串: 直接上代码: var aaa=[ {"name":"张珊","sex":"man"} ...
- Web服务架构风格之REST
REST(Representational State Transfer)是一种Web服务的架构,其目的是创建具有良好扩展性的分布式系统.它的约束包含: 使用C/S模型.client和server之间 ...