Volley Cache缓存机制

来源:互联网 发布:逝去的武林知乎 编辑:程序博客网 时间:2024/05/18 00:51

http://item.congci.com/item/volley-cache-huancun-jizhi

1.http缓存机制

要弄明白volley缓存机制,那么肯定是和浏览器的缓存机制有关了,简单来说volley整套框架要做的事都是模拟浏览器来进行一次次的http交互

1.1.概述

http缓存的是指当Web请求抵达缓存时, 如果本地有“已缓存的”副本,就可以从本地存储设备而不是从原始服务器中提取这个文档。

1.2.与缓存有关的头信息

1.2.1.request:

  • Cache-Control: max-age=0 以秒为单位
  • If-Modified-Since: Mon, 19 Nov 2012 08:38:01 GMT 缓存文件的最后修改时间。
  • If-None-Match: "0693f67a67cc1:0" 缓存文件的Etag值
  • Cache-Control: no-cache 不使用缓存
  • Pragma: no-cache 不使用缓存

1.2.2.response:

  • Cache-Control: public 响应被缓存,并且在多用户间共享
  • Cache-Control: private 响应只能作为私有缓存,不能在用户之间共享
  • Cache-Control:no-cache 提醒浏览器要从服务器提取文档进行验证
  • Cache-Control:no-store 绝对禁止缓存(用于机密,敏感文件)
  • Cache-Control: max-age=60 60秒之后缓存过期(相对时间)
  • Date: Mon, 19 Nov 2012 08:39:00 GMT 当前response发送的时间
  • Expires: Mon, 19 Nov 2012 08:40:01 GMT 缓存过期的时间(绝对时间)
  • Last-Modified: Mon, 19 Nov 2012 08:38:01 GMT 服务器端文件的最后修改时间
  • ETag: "20b1add7ec1cd1:0" 服务器端文件的Etag值

如果同时存在cache-control和Expires怎么办呢?

优先使用cache-control,如果没有cache-control才考虑Expires

2.Volley缓存机制

1.当你add进来一个request的时候,其会根据request.shouldCache(默认为true)进行分发决定是交给NetworkDispatcher还是CacheDispatcher处理

2.NetworkDispatcher通过Network请求数据, 如果有缓存的头信息,会一起发送给服务器

    // Perform the network request. NetworkResponse networkResponse = mNetwork.performRequest(request);public NetworkResponse performRequest(Request<?> request) throws VolleyError {        long requestStart = SystemClock.elapsedRealtime();        while (true) {                .....                .....                .....                addCacheHeaders(headers, request.getCacheEntry());                .....                .....                .....        }    }

3.解析NetworkResponse

 Response<?> response = request.parseNetworkResponse(networkResponse);

在这,得在自定义的request中调用很重要的一个静态方法

HttpHeaderParser.parseCacheHeaders(response)

这个方法主要是将NetworkResponse进行封装成一个Cache.Entry对象

 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);        }        //获取Cache-Control信息        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.equals("must-revalidate") || token.equals("proxy-revalidate")) {                    maxAge = 0;                }            }        }        headerValue = headers.get("Expires");        if (headerValue != null) {            //过期时间(绝对时间)            serverExpires = parseDateAsEpoch(headerValue);        }        //ETag        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;        } else if (serverDate > 0 && serverExpires >= serverDate) {    // Default semantic for Expire header in HTTP specification is softExpire.            softExpire = now + (serverExpires - serverDate);        }        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;    }

4.在NetworkDispatcher中,再根据其shouldCache和是否有缓存实体来判断是否要进行缓存操作

    if (request.shouldCache() && response.cacheEntry != null) {       mCache.put(request.getCacheKey(), response.cacheEntry);   request.addMarker("network-cache-written"); }

5.CacheDispatcher的处理

当收到一个request之后,会先到Cache里面去取,看下是否有缓存,
当出现没有缓存 和 缓存过期的情况就直接丢给NetworkDispatcher来处理;
如果缓存没过期,直接拿到缓存实体丢给request的parseNetworkResponse方法这里调用就和NetworkDispatcher里面处理差不多了;

 @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;                }                continue;            }        }    }
0 0
原创粉丝点击