多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > Volley HTTP 緩存機制

Volley HTTP 緩存機制

來源:程序員人生   發布時間:2016-06-20 07:47:43 閱讀次數:2532次

Volley HTTP 緩存規則

在介紹Volley的HTTP緩存機制之前,我們首先來看1下HTTP HEADER中和緩存有關的字段有:

規則 字段 示例值 類型 作用
新鮮度 Expires Sat, 23 Jul 2016 03:34:17 GMT 響應 告知客戶端在過期時間之前可使用副本
Cache-Control no-cache 響應 告知客戶端疏忽資源的緩存副本,強迫每次要求都訪問服務器
no-store 響應 強迫緩存在任何情況下都不要保存任何副本
must-revalidate 響應 表示必須進行新鮮度的再驗證以后才能使用
max-age=[秒] 響應 指明緩存副本的有效時長,從要求時間到到期時間的秒數
Last-Modified Mon, 23 Jun 2014 08:43:26 GMT 響應 告知客戶端當前資源的最后修改時間
If-Modified-Since Mon, 23 Jun 2014 08:43:26 GMT 要求 如果閱讀器第1次要求時響應的Last-Modified非空,第2次要求同1資源時,會把它作為該項的值發送給服務器
校驗值 ETag 53a7e8ae⑴f79 響應 告知客戶端當前資源在服務器的唯1標識
If-None-Match 53a7e8ae⑴f79 要求 如果閱讀器第1次要求時響應中ETag非空,第2次要求同1資源時,會把它作為該項的值發給服務器

Volley的緩存機制

Volley的緩存機制在對HTTP RESPONSE的解析中能夠明顯的看出來:

public static Cache.Entry parseCacheHeaders(NetworkResponse response) { long now = System.currentTimeMillis(); Map<String, String> headers = response.headers; long serverDate = 0; long lastModified = 0; long serverExpires = 0; long softExpire = 0; long finalExpire = 0; long maxAge = 0; long staleWhileRevalidate = 0; boolean hasCacheControl = false; boolean mustRevalidate = false; String serverEtag; String headerValue; headerValue = headers.get("Date"); if (headerValue != null) { serverDate = parseDateAsEpoch(headerValue); } // 獲得響應體的Cache緩存策略. headerValue = headers.get("Cache-Control"); if (headerValue != null) { hasCacheControl = true; String[] tokens = headerValue.split(","); for (String token : tokens) { token = token.trim(); if (token.equals("no-cache") || token.equals("no-store")) { // no-cache|no-store代表服務器制止客戶端緩存,每次需要重新發送HTTP要求 return null; } else if (token.startsWith("max-age=")) { // 獲得緩存的有效時間 try { maxAge = Long.parseLong(token.substring(8)); } catch (Exception e) { maxAge = 0; } } else if (token.startsWith("stale-while-revalidate=")) { try { staleWhileRevalidate = Long.parseLong(token.substring(23)); } catch (Exception e) { staleWhileRevalidate = 0; } } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) { // 需要進行新鮮度驗證 mustRevalidate = true; } } } // 獲得服務器資源的過期時間 headerValue = headers.get("Expires"); if (headerValue != null) { serverExpires = parseDateAsEpoch(headerValue); } // 獲得服務器資源最后1次的修改時間 headerValue = headers.get("Last-Modified"); if (headerValue != null) { lastModified = parseDateAsEpoch(headerValue); } // 獲得服務器資源標識 serverEtag = headers.get("ETag"); // 計算緩存的ttl和softTtl 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.Entry entry = new Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; entry.ttl = finalExpire; entry.serverDate = serverDate; entry.lastModified = lastModified; entry.responseHeaders = headers; return entry; }

這個方法實際上是實現了Volley的本地緩存的關鍵代碼.


L2級硬盤緩存的實現和緩存替換機制

之前介紹了用戶使用LruCache實現自定義的L1級緩存,而Volley本身利用了FIFO算法實現了L2級硬盤緩存.接下來,就詳細介紹1下硬盤緩存的實現和緩存替換機制.
這里我們也是斟酌如果自己實現硬盤緩存,需要實現哪幾個步驟:

  1. 抽象出存儲實體類.
  2. 定義抽象存儲接口,包括initialize,get,put,clear等具體緩存系統的操作.
  3. 對象的序列化.

存儲實體

存儲的實體肯定是響應的結果,響應結果分為響應頭和響應體,抽象類代碼以下所示:

/** 真正HTTP要求緩存實體類. */ class Entry { /** HTTP響應Headers. */ public Map<String, String> responseHeaders = Collections.emptyMap(); /** HTTP響應體. */ public byte[] data; /** 服務器資源標識ETag. */ public String etag; /** HTTP響應時間. */ public long serverDate; /** 緩存內容最后1次修改的時間. */ public long lastModified; /** Request的緩存過期時間. */ public long ttl; /** Request的緩存新鮮時間. */ public long softTtl; /** 判斷緩存內容是不是過期. */ public boolean isExpired() { return this.ttl < System.currentTimeMillis(); } /** 判斷緩存是不是新鮮,不新鮮的緩存需要發到服務端做新鮮度的檢測. */ public boolean refreshNeeded() { return this.softTtl < System.currentTimeMillis(); } }

抽象緩存系統類

public interface Cache { /** 通過key獲得要求的緩存實體. */ Entry get(String key); /** 存入1個要求的緩存實體. */ void put(String key, Entry entry); void initialize(); void invalidate(String key, boolean fullExpire); /** 移除指定的緩存實體. */ void remove(String key); /** 清空緩存. */ void clear(); }

在Volley中,實現Cache接口的硬盤緩存類是DiskBasedCache.接下來,具體介紹每一個方法的具體實現.

構造函數

我們先來看1下DiskBasedCache的構造函數實現:

public DiskBasedCache(File rootDirectory) { this(rootDirectory, DEFAULT_DISK_USAGE_BYTES); } public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) { mRootDirectory = rootDirectory; mMaxCacheSizeInBytes = maxCacheSizeInBytes; }

類似于LruCache,DiskBasedCache的構造函數做了兩件事:

  1. 指定硬盤緩存的目錄.
  2. 指定硬盤緩存的大小,默許為5M.

initialize函數

在介紹put和get函數之前,先介紹1下硬盤緩存的初始化函數,這個函數主要是用來遍歷緩存的文件,從而獲得當前緩存大小,和構造

/** * 初始化Disk緩存系統. * 作用是:遍歷Disk緩存系統,將緩存文件中的CacheHeader和key存儲到Map對象中. */ public void initialize() { if (!mRootDirectory.exists() && !mRootDirectory.mkdirs()) { // 硬盤緩存目錄不存在直接返回便可 return; } // 獲得硬盤緩存目錄所有文件集合.每一個HTTP要求結果對應1個文件. File[] files = mRootDirectory.listFiles(); if (files == null) { return; } for (File file : files) { BufferedInputStream fis = null; try { fis = new BufferedInputStream(new FileInputStream(file)); // 進行對象反序列化 CacheHeader entry = CacheHeader.readHeader(fis); // 將文件的大小賦值給entry.size,單位字節 entry.size = file.length(); // 在內存中保護1張硬盤<key,value>映照表 putEntry(entry.key, entry); }catch (IOException e) { file.delete(); e.printStackTrace(); }finally { if (fis != null) { try { fis.close(); } catch (IOException ignored) { } } } } } /** 將key和CacheHeader存入到Map對象中.并更新當前占用的總字節數. */ private void putEntry(String key, CacheHeader entry) { if (!mEntries.containsKey(key)) { mTotalSize += entry.size; } else { CacheHeader oldEntry = mEntries.get(key); mTotalSize += (entry.size - oldEntry.size); } mEntries.put(key, entry); }

put函數

接下來我們講授put函數,是由于1個緩存系統最為關鍵的操作就是put,這其中還設計到緩存替換策略的實現.

首先是緩存替換策略.

private void pruneIfNeeded(int neededSpace) { if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) { return; } Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, CacheHeader> entry = iterator.next(); CacheHeader e = entry.getValue(); // 這里的替換策略不太好,其實可以依照serverDate排序,從而實現FIFO的緩存替換策略. boolean deleted = getFileForKey(e.key).delete(); if (deleted) { mTotalSize -= e.size; } iterator.remove(); // 當硬盤大小滿足可以寄存新的HTTP要求結果時,停止刪除操作 if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) { break; } } }

接下來,是硬盤緩存的插入操作,準備是對象序列化的1些內容.

/** 將Cache.Entry存入到指定的緩存文件中. 并在Map中記錄<key,CacheHeader>. */ @Override public synchronized void put(String key, Entry entry) { pruneIfNeeded(entry.data.length); // 根據HTTP的url生成緩存文件(ps:根據hash值生成文件名) File file = getFileForKey(key); try { BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file)); // 這里有個bug,插入時的size只計算響應體,沒有斟酌響應頭部緩存字段的大小. CacheHeader e = new CacheHeader(key, entry); boolean success = e.writeHeader(fos); if (!success) { fos.close(); throw new IOException(); } fos.write(entry.data); fos.close(); putEntry(key, e); return; } catch (IOException e) { e.printStackTrace(); } file.delete(); }

get函數

get函數比較簡單了,源碼以下:

/** 從Disk中根據key獲得并構造HTTP響應體Cache.Entry. */ @Override public synchronized Entry get(String key) { CacheHeader entry = mEntries.get(key); if (entry == null) { return null; } File file = getFileForKey(key); CountingInputStream cis = null; try { cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file))); // 讀完CacheHeader部份,并通過CountingInputStream的bytesRead成員記錄已讀取的字節數. CacheHeader.readHeader(cis); // 讀取緩存文件存儲的HTTP響應體內容. byte[] data = streamToBytes(cis, (int)(file.length() - cis.bytesRead)); return entry.toCacheEntry(data); } catch (IOException e) { remove(key); return null; } finally { if (cis != null) { try { cis.close(); } catch (IOException ignored) { } } } }

clear函數

clear顧名思義,就是清空硬盤緩存的操作:

public synchronized void clear() { File[] files = mRootDirectory.listFiles(); if (files != null) { for (File file : files) { file.delete(); } } mEntries.clear(); mTotalSize = 0; }

所做的事情也比較簡單,包括:

  1. 情況緩存文件.
  2. 將使用size置為0.
  3. 清空內存中保護的硬盤

remove函數

remove函數也就是刪除指定key對應的硬盤緩存,代碼很簡單:

@Override public synchronized void remove(String key) { boolean deleted = getFileForKey(key).delete(); removeEntry(key); if (!deleted) { Log.e("Volley", "沒能刪除key=" + key + ", 文件名=" + getFilenameForKey(key) + "緩存."); } } /** 從Map對象中刪除key對應的鍵值對. */ private void removeEntry(String key) { CacheHeader entry = mEntries.get(key); if (entry != null) { mTotalSize -= entry.size; mEntries.remove(key); } }
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 久久精品亚洲欧美日韩久久 | 日本久久影视 | 欧美黑人巨大videos精 | 亚洲免费在线播放 | 久久久久久岛国免费网站 | 欧美国产日韩一区 | 欧美亚洲国产精品久久蜜芽 | 五月婷婷在线观看视频 | 国产福利自产拍在线观看 | 国产视频一二三区 | 日韩字幕无线乱码 | 欧洲一区二区 | 欧美一级特黄毛片视频 | 欧美一级欧美一级毛片 | 91精品一区二区三区在线观看 | 日本xx18护土| 国产美女精品三级在线观看 | 亚洲影视先锋 | 久久久久777777人人人视频 | 亚洲观看视频 | 亚洲欧美日韩天堂 | 亚洲精品久久久久中文字幕一区 | 亚洲欧美一区二区三区图片 | 欧美偷 | 欧美亚洲网站 | 在线高清视频 | 免费在线视频播放 | 欧美天堂视频 | a丫久久久久久一级毛片 | 国产欧美在线一区二区三区 | 欧美成人三级伦在线观看 | 最近新中文字幕大全高清视频 | 国产成人精品一区二区三区 | 欧美人与牲动交xxxx | 国产精品嫩草影院在线播放 | 欧美高清在线精品一区二区不卡 | 麻豆天堂 | 欧美在线暴力性xxxx | 亚洲 自拍 另类 欧美 综合 | 日本在线一区 | 亚洲精品乱码久久久久久v 亚洲精品乱码久久久久久蜜桃 |