Volley缓存-原理介绍

来源:互联网 发布:菲律宾网络诈骗 编辑:程序博客网 时间:2024/06/10 21:26

本文将从以下方面介绍Volley的缓存机制

  • 使用缓带来哪些优势
  • Volley的缓存原理
  • Volley对服务端header解析
  • Volley缓存过期判断

使用缓带来哪些优势:

  • 速度:已缓存的资源加载的更快;
  • 减少服务器负载:不用每次都和服务器进行交互;
  • 较少网络流量:对于已缓存且在有效期内的数据将不会再次向服务器请求,如果已过期,只需要判断返回的数据是否发生变化,没有变化返回304,告诉客户端可以使用以前的缓存数据,否则返回新的数据;
  • 离线浏览:用户可以在离线时使用;

Volley缓存原理

Volley的缓存原理和浏览器的缓存原理基本一致,基于HTTP协议,通过HTTP协议Header中的Cache-Control(或Expires)和Last-Modified(或Etag)等字段来控制缓存,再使用DiskBasedCache类将数据存储在本地;,下面对Header里面的一些字段进行说明, 服务端:Cache-Control、Expires、max-age、Last-Modified、ETag、stale-while-revalidate、must-revalidate、proxy-revalidate、no-cache、no-store;客户端:If-Modified-Since、If-None-Match;

服务端:

  • Cache-Control:控制是否进行缓存,Http1.1后才出现,可添加的配置信息如下:
    • no-cache或no-store,示列:Cache-Control:no-store或Cache-Control:no-cache表示不缓存数据
    • max-age:缓存有效期(单位秒);示列Cache-Control:max-age=60表示基于当前时间60秒后缓存数据过期
    • stale-while-revalidate:异步缓存机制(单位秒),当请求一个即将过期的文件时,会将本地的过期数据返回给用户,然后在异步请求数据,与must-revalidate或proxy-revalidate配套使用;示列:Cache-Control:stale-while-revalidate=1
    • must-revalidate或proxy-revalidate:表示是否启用stale-while-revalidate字段,有了该字段后stale-while-revalidate才会生效;示列:Cache-Control:stale-while-revalidate=1;must-revalidate
  • Expires:表示在该指定日期前缓存数据有效;示列:Expires:1451273426630
  • Last-Modified:最后一次修改时间,精确到秒;可表示某个文件或者某个请求返回数据的最后修改时间,与If-Modified-Since配套使用,服务端通过取If-Modified-Since值与需要返回的Last-Modified值进行对比判断是否返回304;示列:Last-Modified:1451273426630
  • ETag:表示某个请求或者某个文件的标记,如某个文件或者某个请求返回数据的md5值、hashcode值等,与If-None-Match配套使用,服务端通过取If-None-Match值与需要返回的ETag值进行对比判断是否返回304;示列:ETag:ee4f36eca94844eaebfcc2633f1a8de8

    客户端:

  • If-Modified-Since:最后一次修改时间;值取服务端Last-Modified值,在客户端header中设置该字段,服务端取值后与需要返回的Last-Modified值进行对比判断是否返回304

  • If-None-Match:缓存标识:值取服务端ETag值,在客户端的header中设置该字段,服务端取值后与需要返回的ETag值进行对比判断是否返回304

    Volley在BasicNetwork的addCacheHeaders方法中对If-Modified-Since和If-None-Match进行设置,代码如下:

    private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {        // If there's no cache entry, we're done.        if (entry == null) {            return;        }        if (entry.etag != null) {            headers.put("If-None-Match", entry.etag);        }        if (entry.lastModified > 0) {            Date refTime = new Date(entry.lastModified);            headers.put("If-Modified-Since", DateUtils.formatDate(refTime));        }    }

Volley对服务端header解析

Volly中对服务返回header信息的解析在HttpHeaderParser的parseCacheHeaders方法中,下面展现部分代码:

.........省略部分代码............        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-Control的优先级要高于 Expires

Volley缓存过期判断

Volley的缓存逻辑处理在CacheDispatcher类的run方法中;

  1. 通过Cache.Entry entry = mCache.get(request.getCacheKey());取出缓存信息,如果不存在缓存信息,则将请求放入mNetworkQueue
  2. 通过 Cache.Entry 的isExpired()方法判断缓存是否过期;isExpired()方法代码如下:
        /** True if the entry is expired. */        public boolean isExpired() {            return this.ttl < System.currentTimeMillis();        }

这里的ttl时候为header解析时的finalExpire时间,如果过期,则将请求放入mNetworkQueue;finalExpire取值如下:

        // 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;        }

可以看出finalExpire>=softExpire ,与stale-while-revalidate和must-revalidate或proxy-revalidate的设置有关。

3.通过Cache.Entry的refreshNeeded()方法判断是否需要刷新

        /** True if a refresh is needed from the original data source. */        public boolean refreshNeeded() {            return this.softTtl < System.currentTimeMillis();        }

这里的sotfTtl时间为softExpire,该方法的用途如下:

 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.                            }                        }                    });                }

这段代码表示如果不需要刷新,这将数据直接返回缓存数据,否则,返回缓存数据的同时异步请求新的数据。

0 1
原创粉丝点击