1)Volley可以在SD卡中缓存图片,那可不可以在SD卡中缓存Json数据呢?
2)如果断网了,Volley是不是就不能用了,存在SD卡中的数据是不是就用不了了?
在Volley中,默认使用的缓存实现是 DiskBasedCache,在创建RequestQueue的时候,同时也会创建一个DiskBasedCache对象,如下:
- RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
而缓存文件的位置就是由CacheDir提供的,它的值如下:
在前面的VolleyDemo中,我们加载了一些图片,也加载了一些json数据,那么我们现在就来看看在缓存中的数据,有没有缓存图片的数据,同时,有没有缓存json的数据。
Volley文件夹下面有以下几条数据:
在文件夹中,以"-993813455"开头的文件,其实就是图片文件的缓存,而以-165747开头的那个文件,其实就是我们从天气网站拿下来的json数据的缓存,我们可以打开看一下里面文件的信息,下面是缓存文件-993813455-446463727的内容:
- < http://img.my.csdn.net/uploads/201403/03/1393854084_6138.jpg "FhHq7DMn1v_jbLzbG84-iD_stqzT"榟?E 橮奓 橮奓 X-ViaN 1.1 gzck20:8107 (Cdn Cache Server V2.0), 1.1 dgck149:0 (Cdn Cache Server V2.0) ETag "FhHq7DMn1v_jbLzbG84-iD_stqzT" X-Reqid JAQAAFDYivywH2IT X-Log MC;IO:2 Content-Length 16713 Content-Transfer-Encoding binary
- Connection
- keep-alive Server nginx/1.4.4
- Cache-Control public, max-age=31536000 X-Whom nb5 Date Sat, 05 Apr 2014 16:01:19 GMT Access-Control-Allow-Origin * Content-Disposition& inline; filename="1393854084_6138.jpg" X-Android-Received-Millis
- 1396713679439 Content-Type
- image/jpeg
- Accept-Ranges bytes X-Android-Sent-Millis
- 1396713678988??JFIF d d ?Ducky < ?慼ttp://ns.adobe.com/xap/1.0/ <?xpacket begin="锘? id="W5M0MpCehiHzreSzNTczkc9d"?>
- <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.1-c036 46.276720, Mon Feb 19 2007 22:13:43 ">
- <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <rdf:Description rdf:about=""
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:xapRights="http://ns.adobe.com/xap/1.0/rights/"
- xapRights:Marked="Romain Guy"
- xapRights:WebStatement="Romain Guy">
- <dc:rights>
- <rdf:Alt>
- <rdf:li xml:lang="x-default">Romain Guy</rdf:li>
- </rdf:Alt>
- </dc:rights>
- </rdf:Description>
- </rdf:RDF>
- </x:xmpmeta>
我们可以看到文件的开头就是要展示的图片的url地址,再来看看Json数据的内容:
- 0 http://www.weather.com.cn/data/sk/101280101.html (&?E Age 297 Transfer-Encoding chunked Date Sat, 05 Apr 2014 16:31:37 GMT X-Android-Received-Millis
- 1396715892761 Content-Type text/html; charset=utf-8
- Connection close Server Apache/2.2.0 X-Android-Sent-Millis
- 1396715892611{"weatherinfo":{"city":"骞垮窞","cityid":"101280101","temp":"18","WD":"瑗垮寳椋?,"WS":"1绾?,"SD":"92%","WSE":"1","time":"00:20","isRadar":"1","Radar":"JC_RADAR_AZ9200_JB"}}
不仅可以看到json数据的ulr地址,我们还可以看到下面具体的json数据,如“weatherinfo...”等信息。
从上面这两条数据,我们可以回答那位朋友的第一个问题,SD卡中不仅仅缓存图片,也是有缓存Json数据的。而事实上,通过网络获取来的数据其实都是字节流,Volley只是通过Response的Header信息来设置缓存记录的生命周期等,具体代码如下:
- public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
- long now = System.currentTimeMillis();
-
- Map<String, String> headers = response.headers;
-
- long serverDate = 0;
- long serverExpires = 0;
- long softExpire = 0;
- long maxAge = 0;
- boolean hasCacheControl = false;
-
- String serverEtag = null;
- String headerValue;
-
- headerValue = headers.get("Date");
- if (headerValue != null) {
- serverDate = parseDateAsEpoch(headerValue);
- }
-
- headerValue = headers.get("Cache-Control");
- if (headerValue != null) {
- Log.v(Helper.TAG, "Has Cache-Control");
- 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));
- Log.v(Helper.TAG, "Max AGe = " + maxAge);
- } catch (Exception e) {
- }
- } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
- maxAge = 0;
- }
- }
- }
-
- headerValue = headers.get("Expires");
- if (headerValue != null) {
- serverExpires = parseDateAsEpoch(headerValue);
- }
-
- serverEtag = headers.get("ETag");
-
-
-
- if (hasCacheControl) {
- softExpire = now + maxAge * 1000;
- } else if (serverDate > 0 && serverExpires >= serverDate) {
-
- softExpire = now + (serverExpires - serverDate);
- }
-
- Log.v(Helper.TAG, "SoftExpire = " + softExpire);
-
- Cache.Entry entry = new Cache.Entry();
- entry.data = response.data;
- entry.etag = serverEtag;
- entry.softTtl = softExpire;
- entry.ttl = entry.softTtl;
- entry.serverDate = serverDate;
- entry.responseHeaders = headers;
-
- return entry;
- }
所以,其实不管数据是图片,还是Json数据,对于缓存来说,这并不重要。
那么我们来看看第二个问题,断网了,Volley是不是就不能用了?
通过前面Volley的框架图,我们知道,Volley首先会去缓存中找数据,如果找不到才会去网络中获取数据,所以如果缓存中有数据的话,它就不用去跟网络打交道了,那么跟断不断网其实也就关系不大了。
把手机设置成飞行模式,然后我们再去加载图片,我们可以看到在Log中的信息,如下:
- 04-06 11:44:43.671: D/Volley(6541): [1] MarkerLog.finish: (+0 ) [ 1] add-to-queue
- 04-06 11:44:43.671: D/Volley(6541): [1] MarkerLog.finish: (+434 ) [1059] cache-queue-take
- 04-06 11:44:43.671: D/Volley(6541): [1] MarkerLog.finish: (+17 ) [1059] cache-hit
- 04-06 11:44:43.671: D/Volley(6541): [1] MarkerLog.finish: (+9 ) [1059] cache-hit-parsed
- 04-06 11:44:43.671: D/Volley(6541): [1] MarkerLog.finish: (+1 ) [1059] post-response
- 04-06 11:44:43.681: D/Volley(6541): [1] MarkerLog.finish: (+0 ) [ 1] done
- 04-06 11:44:43.691: V/com.lms.volleydemo(6541): Has Cache-Control
- 04-06 11:44:43.691: V/com.lms.volleydemo(6541): Max AGe = 31536000
- 04-06 11:44:43.691: V/com.lms.volleydemo(6541): SoftExpire = 1428291883693
- 04-06 11:44:43.691: V/com.lms.volleydemo(6541): Completely unexpired cache hit. http:
可以看到Volley的确是可以从缓存中拿出图片数据来展示的,相信对于这个问题的答案,那位朋友应该也了解了吧。
那么我们再来看看Json数据的log吧,如下:
- 04-06 12:18:37.521: V/com.lms.volleydemo(6541): cache-hit-expired for request http:
- 04-06 12:18:37.551: D/Volley(6541): [1] MarkerLog.finish: (99 ms) [ ] http:
- 04-06 12:18:37.551: D/Volley(6541): [1] MarkerLog.finish: (+0 ) [ 1] add-to-queue
- 04-06 12:18:37.551: D/Volley(6541): [1] MarkerLog.finish: (+1 ) [1059] cache-queue-take
- 04-06 12:18:37.551: D/Volley(6541): [1] MarkerLog.finish: (+74 ) [1059] cache-hit-expired
- 04-06 12:18:37.551: D/Volley(6541): [1] MarkerLog.finish: (+1 ) [1062] network-queue-take
- 04-06 12:18:37.551: D/Volley(6541): [1] MarkerLog.finish: (+20 ) [1062] post-error
- 04-06 12:18:37.551: D/Volley(6541): [1] MarkerLog.finish: (+3 ) [ 1] done
- 04-06 12:18:38.683: D/memalloc(6541): ion: Mapped buffer base:0x5c2a9000 size:3768320 offset:0 fd:51
在飞行模式下,我们看到它首先是cache_hit_expired,然后才会去网络获取数据,但是因为没有连网,所以就会出现错误,它就获取不到数据了。
那么Volley中是如何判断expired的数据的呢,如下:
-
- public boolean isExpired() {
- return this.ttl < System.currentTimeMillis();
- }
而ttl的值就是上面parseCacheHeader中的softExpire的值了,这个值是由缓存中的MaxAge的值来计算的,但其实所有这些都是从Response中取出来的值。
对应于我们demo中的天气预报的url,通过网络传回来的response是没有cache-control的,所以它的maxAge也就是0了,从而导致ttl的值也一直是0,所以就一直是过期的,需要去从网张中获取。
我觉得这是因为天气是瞬息万变的,搞缓存是没有意义的,而对于其它的json数据,其实取决于服务器传回来的Header信息中CacheControl的信息。
当然,如果真想自己缓存,又不想它过期,可以把Volley的源代码也导入项目中,修改源代码也是可以的,不过这就。。。。