Volley的核心 是两个队列 :CacheQueue和NetworkQueue;
Request先添加到CacheQueue里,然后被CacheDispatcher轮询,查到如果有缓存,就直接解析出respons对象,没有哦就添加到NetworkQueue中,被NetworkDispathcer轮询,**默认有4个NetworkDisatcher来轮询**NetworkQueue,每个Dispathcer都是 一个线程,
所以main thread, cache thread 和4个network thread
其中的两个Queue,都是一个BlockingQueue
这是BlockingQueue的特性 是:如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒,同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间时才会被唤醒继续操作。
网络执行单元,Volley提供了httpStack接口,并提供了HurlStack和HurlClientStack两个实现类,(此处就是开放性接口,比如可以接入okHttpStack)
缺陷: 1.缓存依靠服务器协议,不保准
2.get和post请求方式需要封装,加个单例
3.要过滤重复请求
4.ImageLoader没做缓存,只有接口
缓存
Volley的缓存是基于标准的http协议的,也就是在请求头中相关缓存的信息,比如:
expires字段:缓存的过期时间
Cache-Control 字段:控制是否有缓存或者是否需要先验证等
Last-Modified : 最后修改时间
所以,Volley缓存处 的关键源码:
public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
long now = System.currentTimeMillis();
Map 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;
}
显然,就是获取了Cache-Control值,判断是否缓存,在获取时间,判断是否过期,然后用一个entry 存储相关信息。
注意事项
在面板被finish掉的时候(比如onStop方法),要调用
requestQueue.canceAll( tag ), 此处的Tag是你的request在加入队列之前,setTag设置的, 所以是取消相同tag的所以请求。
取消后 ,onResponse不会被执行。