日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內(nèi)最全IT社區(qū)平臺 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當前位置:首頁 > php開源 > 綜合技術(shù) > Volley HTTP 緩存機制

Volley HTTP 緩存機制

來源:程序員人生   發(fā)布時間:2016-06-20 07:47:43 閱讀次數(shù):2540次

Volley HTTP 緩存規(guī)則

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

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

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代表服務(wù)器制止客戶端緩存,每次需要重新發(fā)送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; } } } // 獲得服務(wù)器資源的過期時間 headerValue = headers.get("Expires"); if (headerValue != null) { serverExpires = parseDateAsEpoch(headerValue); } // 獲得服務(wù)器資源最后1次的修改時間 headerValue = headers.get("Last-Modified"); if (headerValue != null) { lastModified = parseDateAsEpoch(headerValue); } // 獲得服務(wù)器資源標識 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; }

這個方法實際上是實現(xiàn)了Volley的本地緩存的關(guān)鍵代碼.


L2級硬盤緩存的實現(xiàn)和緩存替換機制

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

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

存儲實體

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

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

抽象緩存系統(tǒng)類

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中,實現(xiàn)Cache接口的硬盤緩存類是DiskBasedCache.接下來,具體介紹每一個方法的具體實現(xiàn).

構(gòu)造函數(shù)

我們先來看1下DiskBasedCache的構(gòu)造函數(shù)實現(xiàn):

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

類似于LruCache,DiskBasedCache的構(gòu)造函數(shù)做了兩件事:

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

initialize函數(shù)

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

/** * 初始化Disk緩存系統(tǒng). * 作用是:遍歷Disk緩存系統(tǒng),將緩存文件中的CacheHeader和key存儲到Map對象中. */ public void initialize() { if (!mRootDirectory.exists() && !mRootDirectory.mkdirs()) { // 硬盤緩存目錄不存在直接返回便可 return; } // 獲得硬盤緩存目錄所有文件集合.每一個HTTP要求結(jié)果對應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,單位字節(jié) entry.size = file.length(); // 在內(nèi)存中保護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對象中.并更新當前占用的總字節(jié)數(shù). */ 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函數(shù)

接下來我們講授put函數(shù),是由于1個緩存系統(tǒng)最為關(guān)鍵的操作就是put,這其中還設(shè)計到緩存替換策略的實現(xiàn).

首先是緩存替換策略.

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排序,從而實現(xiàn)FIFO的緩存替換策略. boolean deleted = getFileForKey(e.key).delete(); if (deleted) { mTotalSize -= e.size; } iterator.remove(); // 當硬盤大小滿足可以寄存新的HTTP要求結(jié)果時,停止刪除操作 if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) { break; } } }

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

/** 將Cache.Entry存入到指定的緩存文件中. 并在Map中記錄<key,CacheHeader>. */ @Override public synchronized void put(String key, Entry entry) { pruneIfNeeded(entry.data.length); // 根據(jù)HTTP的url生成緩存文件(ps:根據(jù)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函數(shù)

get函數(shù)比較簡單了,源碼以下:

/** 從Disk中根據(jù)key獲得并構(gòu)造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成員記錄已讀取的字節(jié)數(shù). CacheHeader.readHeader(cis); // 讀取緩存文件存儲的HTTP響應體內(nèi)容. 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函數(shù)

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. 清空內(nèi)存中保護的硬盤

remove函數(shù)

remove函數(shù)也就是刪除指定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); } }
生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: www.成人在线视频 | 日韩欧美亚洲综合 | 亚洲欧美不卡 | 亚洲免费黄色 | 亚洲精品乱码久久久久久9色 | 亚洲高清在线视频 | 日韩视频在线一区二区 | 国产欧美在线观看 | 91亚洲精品乱码久久久久久蜜桃 | 性欧美另类| 色久视频| 亚洲欧美一区二区在线观看 | av国产片 | 欧美日本韩国在线 | 91精品国产综合久久久久 | 青青草成人网 | 亚洲精品成人av | 欧美黑人巨大videos精品 | 久久99精品久久久久久园产越南 | 99久久精品免费看国产免费软件 | 91福利网站 | 欧美香蕉网 | av片在线看免费高清网站 | www.亚洲一区| 国产黄大片在线观看 | 另类 欧美 日韩 国产 在线 | 在线a毛片免费视频观看 | 黄在线网站| 热99精品 | 中文字幕在线不卡视频 | 亚洲 欧美 日韩 在线 | 久久久网| 久久免费国产精品 | 91视频精品 | 国产专区精品 | 国产日韩一区二区三区 | 日韩福利一区二区 | 亚洲综合欧美 | 日韩精品视频免费观看 | 看a网站 | 久久国产精品精品国产色婷婷 |