public static Cache.Entry parseCacheHeaders(NetworkResponse response) { Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); if (entry == null) { entry = new Cache.Entry(); entry.data = response.data; } return entry; }
@Test public void testCachePutExpired() throws VolleyError { when(cacheEntry.isExpired()).thenReturn(true); // mock non expired entry for return Cache.Entry networkEntry = mock(Cache.Entry.class); when(networkEntry.isExpired()).thenReturn(true); Response response = Response.success(null, networkEntry); when(request.parseNetworkResponse(any(NetworkResponse.class))).thenReturn(response); dispatcher.processRequest(request); verify(cache, never()).put("test-cache-key", networkEntry); }
/** * Extracts a {@link com.android.volley.Cache.Entry} from a {@link * com.android.volley.NetworkResponse}. * * @param response The network response to parse headers from * @param cacheTime 缓存时间,如果设置了这个值,不管服务器返回是否可以缓存,都会缓存,一天为1000*60*60*24 * @return a cache entry for the given response, or null if the response is not cacheable. */ public static Cache.Entry parseCacheHeaders(NetworkResponse response, long cacheTime) { long now = System.currentTimeMillis(); long softExpire = now + cacheTime; Cache.Entry entry = parseCacheHeaders(response); if (null == entry && response.data.length > 0) { entry = new Cache.Entry(); entry.data = response.data; entry.serverDate = now; // Time to Leave 客户端可以通过这个数据来判断当前内容是否过期。 entry.ttl = softExpire; // 在这个时间内,是否需要刷新 entry.softTtl = softExpire; } return entry; }
@Test public void testRefresh() throws VolleyError { when(cacheEntry.isExpired()).thenReturn(false); when(cacheEntry.refreshNeeded()).thenReturn(true); // mock non expired entry for return Cache.Entry networkEntry = mock(Cache.Entry.class); when(networkEntry.isExpired()).thenReturn(false); Response response = Response.success(null, networkEntry); when(request.parseNetworkResponse(any(NetworkResponse.class))).thenReturn(response); request.setShouldCache(true); dispatcher.processRequest(request); verify(cache).put("test-cache-key", networkEntry); verify(mockNetwork).performRequest(request); }
public static Cache.Entry parseIgnoreCacheHeaders( NetworkResponse response, String url, long softttl, long hardttl) { long now = System.currentTimeMillis(); Map<String, String> headers = response.headers; long serverDate = 0; String serverEtag = null; String headerValue; headerValue = headers.get("Date"); if (headerValue != null) { serverDate = parseDateAsEpoch(headerValue); } serverEtag = headers.get("ETag"); // entry expires // completely final long softExpire = now + softttl; final long ttl = now + hardttl; Cache.Entry entry = new Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; entry.ttl = ttl; entry.serverDate = serverDate; entry.responseHeaders = headers; return entry; }
/** * Extracts a {@link Cache.Entry} from a {@link com.android.volley.NetworkResponse}. Cache-control * headers are ignored. SoftTtl == 3 mins, ttl == 24 hours. * * @param response The network response to parse headers from * @return a cache entry for the given response, or null if the response is not cacheable. */ public static Cache.Entry parseIgnoreCacheHeaders(NetworkResponse response) { long now = System.currentTimeMillis(); Map<String, String> headers = response.headers; long serverDate = 0; String serverEtag = null; String headerValue; headerValue = headers.get("Date"); if (headerValue != null) { serverDate = com.android.volley.toolbox.HttpHeaderParser.parseDateAsEpoch(headerValue); } serverEtag = headers.get("ETag"); final long cacheHitButRefreshed = 3 * 60 * 1000; // in 3 minutes cache will be hit, but also refreshed on background final long cacheExpired = 24 * 60 * 60 * 1000; // in 24 hours this cache entry expires completely final long softExpire = now + cacheHitButRefreshed; final long ttl = now + cacheExpired; Cache.Entry entry = new Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; entry.ttl = ttl; entry.serverDate = serverDate; entry.responseHeaders = headers; return entry; }
@Test public void testCachePut() throws VolleyError { // Given when(cacheEntry.isExpired()).thenReturn(true); // mock non expired entry for return Cache.Entry networkEntry = new Cache.Entry(); networkEntry.ttl = System.currentTimeMillis() + 100000; NetworkResponse networkResponse = mock(NetworkResponse.class); when(mockNetwork.performRequest(any(Request.class))).thenReturn(networkResponse); Response response = Response.success(new Object(), networkEntry); when(request.parseNetworkResponse(any(NetworkResponse.class))).thenReturn(response); request.setShouldCache(true); // When dispatcher.processRequest(request); // Then verify(cache).put("test-cache-key", networkEntry); }
@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)); // TODO 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; } } }
/** * Extracts a {@link Cache.Entry} from a {@link NetworkResponse}. * * @param response The network response to parse headers from * @return a cache entry for the given response, or null if the response is not cacheable. */ 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; long lastModify = 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) { 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); } 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); } headerValue = headers.get("Last-Modified"); if (headerValue != null) { lastModify = parseDateAsEpoch(headerValue); } Cache.Entry entry = new Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; entry.ttl = entry.softTtl; entry.serverDate = serverDate; entry.lastModify = lastModify; entry.responseHeaders = headers; return entry; }
@Test public void testNetworkAccess() throws VolleyError { when(cacheEntry.isExpired()).thenReturn(true); dispatcher.processRequest(request); verify(mockNetwork).performRequest(request); }