Android下的緩存策略

Android下的緩存策略

內存緩存

經常使用的內存緩存是軟引用和弱引用,大部分的使用方式是Android提供的LRUCache緩存策略,本質是個LinkedHashMap(會根據使用次數進行排序)java

磁盤緩存

DiskLruCache:非谷歌官方編寫,可是得到官方認證android

  • 不限制數據緩存的位置,可自由的設置,一般狀況下會選擇:/sdcard/Android/data/{packageName}/cache這個路徑
選擇這個位置的好處
  1. 這是存儲在sdcard上的,只要sdcard空間足夠,不會對手機內置存儲有任何影響
  2. 該路徑被Android系統認定爲應用程序的緩存路徑,當app被卸載時,這裏的數據會被一塊兒清除掉,而不會出現刪除app後,還殘留數據的問題

getCacheDir()是獲取app在手機內部存儲的cache目錄
getFilesDir()是獲取app在手機內部存儲的files目錄算法

經過Context.getExternalFilesDir()能夠獲取到app在sdcard上的files目錄,一般用於存放要長時間保存的數據
經過Context.getExternalCacheDir()能夠獲取到app在sdcard上的cache目錄,一般用於存放一些臨時數據
使用上面兩個api,在app被卸載的時候,在sdcard上對應的全部文件也會自動被刪除,不會留下垃圾信息
並且上面兩個目錄在設置裏的應用詳情裏,可使用清除數據和清除緩存來清理臨時文件api

LruCache——內存緩存策略

LRU(Least Recently Used)緩存算法,近期最少使用的算法
LruCache是Android 3.1之後提供的一個緩存類數組

LruCache的介紹

  • LruCache是 一個泛型類,主要原理是把最近使用的對象用強引用的方式存儲在LinkedHashMap中,把最近最少使用的對象從內存中移除,並提供get和put方法來完成緩存的獲取和添加操做

LruCache的緩存大小通常爲當前進程可用容量的1/8
重寫sizeOf方法,計算每一個緩存對象的大小
注意:緩存的總容量和每一個緩存對象的大小所用的單位要一致緩存

LruCahe的實現原理

  • 維護一個緩存對象列表,按照訪問順序進行排序
  • 一直沒有訪問的對象放在隊尾,即將被淘汰
  • 最近訪問的對象放在隊首,最後被淘汰
  • 這個隊列由LinkedHashMap來維護

LinkedHashMap

  • 是由數組+雙向鏈表的數據結構來實現
  • 雙向鏈表的結構能夠實現訪問順序和插入順序,使得隊列中的對象按照必定的順序排列起來

在構造函數中,可使用accessOrder參數來控制雙向鏈表的結構是訪問順序仍是插入順序
其中accessOrder設置爲true則爲訪問順序,爲false,則爲插入順序。
設置LinkedHashMap的accessOrder爲true,並向裏面插入數據後隨機訪問數據,將訪問數據後的LinkedHashMap輸出,最近訪問數據的最後輸出網絡

LruCache的源碼分析

LruCache內部使用LinkedHashMap的訪問順序特性,來緩存數據
當調用put()方法時,就會在集合中添加元素,並調用trimToSize()來判斷緩存是否已滿,若是滿了就刪除隊尾元素
當調用get()方法時,就會調用LinkedHashMap的get()方法得到對應的元素,同時會更新該元素到隊首數據結構

DiskLruCache——磁盤緩存策略

DiskLruCache目前還不是Android SDK的一部分,可是Android官方文檔推薦使用該算法來實現磁盤緩存app

DiskLruCache的使用方法

打開

DiskLruCache不能new出實例,須要調用它的open()方法
open()方法接收四個參數:函數

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
  • directory:數據的緩存地址
  • appVersion:當前應用程序的版本號
  • valueCount:同一個key能夠對應多少個緩存文件,基本都是傳1
  • maxSize:最多能夠緩存多少字節的數據
  • 其中緩存地址前面已經說過了,一般都會存放在 /sdcard/Android/data/ /cache 這個路徑下面,但同時咱們又須要考慮若是這個手機沒有SD卡,或者SD正好被移除了的狀況,所以比較優秀的程序都會專門寫一個方法來獲取緩存地址
public File getDiskCacheDir(Context context, String uniqueName) {  
    String cachePath;  
    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())  
            || !Environment.isExternalStorageRemovable()) {  
        cachePath = context.getExternalCacheDir().getPath();  
    } else {  
        cachePath = context.getCacheDir().getPath();  
    }  
    return new File(cachePath + File.separator + uniqueName);  
}
  • 能夠看到,當SD卡存在或者SD卡不可被移除的時候,就調用getExternalCacheDir()方法來獲取緩存路徑,不然就調用getCacheDir()方法來獲取緩存路徑。前者獲取到的就是 /sdcard/Android/data/ /cache 這個路徑,然後者獲取到的是 /data/data/ /cache 這個路徑。

  • 接着是應用程序版本號,咱們可使用以下代碼簡單地獲取到當前應用程序的版本號:

public int getAppVersion(Context context) {  
    try {  
        PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);  
        return info.versionCode;  
    } catch (NameNotFoundException e) {  
        e.printStackTrace();  
    }  
    return 1;  
}
  • 須要注意的是,每當版本號改變,緩存路徑下存儲的全部數據都會被清除掉,由於DiskLruCache認爲當應用程序有版本更新的時候,全部的數據都應該從網上從新獲取。
DiskLruCache mDiskLruCache = null;  
try {  
    File cacheDir = getDiskCacheDir(context, "bitmap");  
    if (!cacheDir.exists()) {  
        cacheDir.mkdirs();  
    }  
    mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);  
} catch (IOException e) {  
    e.printStackTrace();  
}
插入緩存

插入使用DiskLruCache.Editor這個類來完成,一樣,它也不能new出實例,須要使用edit()方法

public Editor edit(String key) throws IOException
  • 能夠看到,edit()方法接收一個參數key,這個key將會成爲緩存文件的文件名,而且必需要和圖片的URL是一一對應的。那麼怎樣才能讓key和圖片的URL可以一一對應呢?直接使用URL來做爲key?不太合適,由於圖片URL中可能包含一些特殊字符,這些字符有可能在命名文件時是不合法的。其實最簡單的作法就是將圖片的URL進行MD5編碼,編碼後的字符串確定是惟一的,而且只會包含0-F這樣的字符,徹底符合文件的命名規則。
讀取緩存
// get()方法要求傳入一個key來獲取到相應的緩存數據,而這個key毫無疑問就是將圖片URL進行MD5編碼後的值了
public synchronized Snapshot get(String key) throws IOException
  • 經過get()方法,會獲取到一個DiskLruCache.Snapshot對象,經過調用它的getInputStream()方法就能夠獲得緩存文件的輸入流
移除緩存
public synchronized boolean remove(String key) throws IOException
  • 這個方法咱們並不該該常常去調用它。由於你徹底不須要擔憂緩存的數據過多從而佔用SD卡太多空間的問題,DiskLruCache會根據咱們在調用open()方法時設定的緩存最大值來自動刪除多餘的緩存。只有你肯定某個key對應的緩存內容已通過期,須要從網絡獲取最新數據的時候才應該調用remove()方法來移除緩存。
其餘api
  • size():返回當前緩存路徑下全部緩存數據的總字節數數,用於在app上顯示當前緩存的總大小
  • flush():用於將內存中的操做同步到日誌文件中(也就是journal文件),所以DiskLruCache可以正常工做的前提是要依賴於journal文件中的內容。頻繁的調用會增長同步journal文件的時間,比較標準的作法就是愛Activity的onPause()方法中調用一次便可
  • close():用於將DiskLruCache關閉,和open()方法相對應。關閉掉了以後就不能再調用DiskLruCache中任何操做緩存數據的方法,一般只應該在Activity的onDestroy()方法中去調用close()方法。
  • delete():這個方法用於將全部的緩存數據所有刪除,用於讓用戶清除緩存

參考文檔:

http://blog.csdn.net/guolin_blog/article/details/28863651
http://blog.csdn.net/guolin_blog/article/details/34093441

相關文章
相關標籤/搜索