2014-05-19 40 views
1

我將很快開展一個項目,該項目主要使用HTTPRequests主要是JSON s和Images,所以我認爲考慮緩存是一個好主意。基本上,我正在尋找一個HttpResponseCache不清除舊文件?

    解決
  1. 與給定終身啓動HTTPRequest(FE 3,6,12小時)
  2. 檢查,如果該請求是在緩存中可用的並且仍有效(壽命)
  3. 如果申請仍然有效,把它從緩存,否則發出請求,並保存其響應

我發現HttpResponseCache類的Android。它正在工作,但它是而不是像我期待的那樣工作。

我的測試用例是一個AsyncTask緩存幾個圖像。代碼如下所示:

URL url = new URL(link); 
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 

Bitmap myBitmap; 
try { 
    connection.addRequestProperty("Cache-Control","only-if-cached"); 

    //check if Request is in cache  
    InputStream cached = connection.getInputStream(); 

    //set image if in cache 
    myBitmap = BitmapFactory.decodeStream(cached); 

} catch (FileNotFoundException e) { 
    HttpURLConnection connection2 = (HttpURLConnection) url.openConnection(); 
    connection2.setDoInput(true); 
    connection2.addRequestProperty("Cache-Control", "max-stale=" + 60); 
    connection2.connect(); 

    InputStream input = connection2.getInputStream(); 
    myBitmap = BitmapFactory.decodeStream(input); 

} 

return myBitmap; 

} catch (IOException e) { 
    e.printStackTrace();   
} 

兩個問題:

  1. 我設置max-stale=60seconds用於測試目的。但是,如果我在5分鐘後調用相同的URL,它告訴我,它正在從緩存中加載圖像。 我會假設,由於緩存中的HTTPRequest已過期,圖像會被重新加載?或者我必須自己清理緩存嗎?
  2. 在我的catch塊中,我必須創建第二個HttpURLConnection,因爲在打開URLConnection(發生在connection.getInputStream()?!)中後,我無法添加屬性。這是不好的編程?

畢竟,我發現HttpResponseCache記錄不當。我遇到了Volley: Fast Networking,但這似乎更少記錄,即使它提供了我需要的東西(請求排隊和優先級...)。 你對緩存有什麼用處?歡迎任何與圖書館,教程的鏈接。

UPDATE 我並未指定的Android版本低於4.0(仍可能intresting爲其他用戶?)

回答

2

HttpResponseCachevolley記錄不完整。不過,我發現你可以很容易的擴展和調整volley。如果您研究凌空抽象源代碼,尤其是:CacheEntry,CacheDispatcherHttpHeaderParser,您可以看到它是如何實現的。

CacheEntry保持serverDateetagttlsofTtl其可以表示高速緩存狀態非常好,還具有isExpired()refreshNeeded()方法爲方便。

CacheDispatcher準確地實施,以及:

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

一個有趣的花絮:如果緩存是「軟過期」,凌空將立即提供從本地緩存中的數據,並從服務器一段時間後,再重新發貨,針對單個請求。

最後,HttpHeaderParser會盡力應付到服務器頭:

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

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; 

所以,確保服務器發送適當的報頭,以及榮譽ETAG,時間戳和高速緩存控制頭。

最後,您可以覆蓋getCacheEntry()Request類來返回自定義CacheEntry使緩存行爲完全根據您的需要。

1

要啓用緩存,所有你需要做的只是通過安裝在應用程序啓動HTTP響應緩存使用下面的代碼:

File httpCacheDir = new File(context.getCacheDir(), "http"); 
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB 
HttpResponseCache.install(httpCacheDir, httpCacheSize); 

資源是否需要從網絡或高速緩存中取出,由HttpResponseCache照顧。緩存的年齡在資源請求的響應報頭中指定。例如,這個image指定緩存時間爲43200秒。

您可以驗證資源是否從高速緩存或網上下載的,通過使用以下API:

關於max-stale,您誤解了它的用途。它被用來允許陳舊的緩存響應。下面是它的自定義rfc documentation

指示客戶願意接受已 超過其到期時間的響應。如果max-stale分配了一個值,那麼客戶端願意接受超過其期限時間不超過指定秒數的響應。如果沒有將 值分配給max-stale,那麼客戶願意接受任何年齡的陳舊響應 。

關於緩存控制指令only-if-cached,僅當您的應用程序正在下載最新內容時需要顯示內容時才使用它。所以不會出現在異常處理程序中處理新HttpUrlConnection的問題。從docs

有時你會想要顯示資源,如果他們立即可用 ,但不是。這可以用於您的應用程序 可以顯示某些內容,以等待最新的數據下載。 要限制對本地高速緩存的資源的請求,添加 只,如果緩存指令

一個建議,加finally塊,在那裏你釋放通過調用disconnect連接。