Volley的缓存策略

Posted ihrthk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Volley的缓存策略相关的知识,希望对你有一定的参考价值。

使用分析的库为:com.mcxiaoke.volley:library:1.0.19

0.如果开启缓存(默认为开启)

//Request.java

/** Whether or not responses to this request should be cached. */
private boolean mShouldCache = true;

/**
 * Set whether or not responses to this request should be cached.
 *
 * @return This Request object to allow for chaining.
 */
public final Request<?> setShouldCache(boolean shouldCache) 
    mShouldCache = shouldCache;
    return this;

1. 获取Cache.Entry,如果为null,就请求网络

//CacheDispatcher.java

Cache.Entry entry = mCache.get(request.getCacheKey());

2.默认缓存的硬过期和软过期相同(ttl=softtl)

//HttpHeaderParser.java

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

3. 判断缓存硬过期和软过期的方法

/** True if the entry is expired. */
public boolean isExpired() 
    return this.ttl < System.currentTimeMillis();


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

4. 如果缓存硬过期就请求网络

//CacheDispatcher.java

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


/** True if the entry is expired. */
public boolean isExpired() 
    return this.ttl < System.currentTimeMillis();


public long getTtl()
    long ttl;
    if (hasCacheControl())
        softExpire = now + maxAge * 1000;
        ttl = mustRevalidate ? softExpire : softExpire + staleWhileRevalidate * 1000;
     else 
        ttl = now + (serverExpires - serverDate);
    
    return ttl;


public boolean hasCacheControl()
    String headerValue = headers.get("Cache-Control");
    boolean hasCacheControl = headerValue != null;
    return hasCacheControl;

/*softExpire和softTtl是一样的*/
public long getSoftTtl()
    boolean softExpire = hasCacheControl() ? now + maxAge * 1000 : now + (serverExpires - serverDate);
    return softExpire;

5. 如果缓存没有硬过期,则解析缓存数据

//CacheDispatcher.java

// 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");

6.接下来判断缓存是否软过期,如果缓存没有软过期,则直接返回缓存的数据;如果已经过期了,则先返回缓存数据,再次请求网络

//CacheDispatcher.java

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.
    final Request<?> finalRequest = request;
    mDelivery.postResponse(request, response, new Runnable() 
        @Override
        public void run() 
            try 
                mNetworkQueue.put(finalRequest);
             catch (InterruptedException e) 
                // Not much we can do about this.
            
        
    );


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

/*softExpire和softTtl是一样的*/
public long getSoftTtl()
    boolean softExpire = hasCacheControl() ? now + maxAge * 1000 : now + (serverExpires - serverDate);
    return softExpire;

7.如果的响应的statusCode为304,并且响应已经返回过一次,则忽略这个请求(即不在主线程callback)。否则,继续解析解析网络数据,并且返回(这就会出现回调两次callback的情况)。

//NetworkDispatcher.java

// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) 
    request.finish("not-modified");
    continue;

补充:
那服务端如何判断304的呢?
就需要先添加两个请求头
If-None-Match(从上次的响应头ETag获取)
If-Modified-Since(从上次的响应头Last-Modified获取)

8.最后,如果请求使用缓存,则把获取的网络数据,保存到本地。

//NetworkDispatcher.java

// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
if (request.shouldCache() && response.cacheEntry != null) 
    mCache.put(request.getCacheKey(), response.cacheEntry);
    request.addMarker("network-cache-written");

附一手写的图,来理解ttl和softttl的关系

以上是关于Volley的缓存策略的主要内容,如果未能解决你的问题,请参考以下文章

当重试策略设置为 0 时,Android Volley 向服务器发出 2 个请求

Volley详解+源码分析

Volley -- 源码分析

使用 Volley 和 OkHttp 时如何配置 Http 缓存?

volley2--volley的使用和架构

Volley HTTP 缓存机制