DiskLruCache詳解

上一節(jié),我們了解了LruCache的使用糙及,我們知道LruCache是從內(nèi)存中去獲取緩存信息豪嗽。但內(nèi)存大小是有限的珠十,因此無法存取大量的緩存。從而荠诬,引入了DiskLruCache琅翻,磁盤緩存,將信息緩存到手機中柑贞。

LruCache和DiskLruCache的區(qū)別:
  • 共同點:兩者都是緩存信息方椎,并且都是采用LRU算法。

  • 不同點:LruCache讀寫速度快钧嘶,但存儲大小有限棠众。DiskLruCache讀寫速度慢,但存儲空間大有决。

DiskLruCache的創(chuàng)建:

DiskLruCache并不能通過構(gòu)造方法來創(chuàng)建闸拿,它提供了open方法用于創(chuàng)建自身:

/**
* 用于創(chuàng)建DiskLruCache
* 參數(shù)一:表示磁盤在文件系統(tǒng)中存儲的路徑
* 參數(shù)二:表示應用的版本號,一般設為1书幕,當版本號發(fā)生改變時新荤,會將之前的緩存信息清空
* 參數(shù)三:表示單個節(jié)點所對應的數(shù)據(jù)的個數(shù),一般設為1
* 參數(shù)四:表示緩存的總大小台汇,當緩存大小超過這個設定值后苛骨,DiskLruCache會清除一些緩存,從而保證不會超過這個設定值
*/
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize);

具體實現(xiàn)如下:

//定義DiskLruCache
private DiskLruCache mDiskLruCache;
//設定DiskLruCache的大小,這里設置為50MB
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;
//創(chuàng)建緩存文件夾
File diskCacheDir = getDiskCacheDir(mContext, "bitmap");
//若緩存文件夾不存在苟呐,則創(chuàng)建文件夾
if (!diskCacheDir.exists()){
    diskCacheDir.mkdirs();
}
//創(chuàng)建DiskLruCache
mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);

public File getDiskCacheDir(Context Context, String uniqueName){
    //檢查手機是否支持外部存儲
    boolean externalStorageAvailable = Environment.getExternalStorageState()
                .equals(Environment.MEDIA_MOUNTED);
    final String cachePath;
    //若存在外部存儲痒芝,則獲取到其對應的文件
    if (externalStorageAvailable) {
        cachePath = context.getExternalCacheDir().getPath();
    } 
    //否則獲取內(nèi)部存儲緩存文件夾
    else {
        cachePath = context.getCacheDir().getPath();
    }
    //返回緩存文件夾
    return new File(cachePath + File.separator + uniqueName);
}
DiskLruCache的緩存添加:

DiskLruCache的緩存添加的操作是通過Editor完成的,Editor表示一個緩存對象的編輯對象牵素。

我們以圖片緩存為例严衬,首先需要獲取圖片url所對應的key,然后根據(jù)key就可以通過edit()來獲取Editor對象笆呆。如果這個對象正在被編輯请琳,那么edit()會返回null,即DiskLruCache不允許同時編輯一個緩存對象腰奋。

因為url中可能含有特殊字符单起,這樣url在Android中直接使用抱怔,一般采用url的md5值作為key劣坊。

private String hashKeyFromUrl(String url){
    String cacheKey;
    try{
        final MessageDigest mDigest = MessageDigest.getInstance("MD5");
        mDigest.update(url.getBytes());
        cacheKey = bytesToHexString(mDigest.digest());
    } catch(NoSuchAlgorithmException e){
        cacheKey = String.valueOf(url.hashCode());
    }
    return cacheKey;
}

private String bytesToHexString(byte[] bytes){
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < bytes.length ; i ++ ) {
        String hex = Integer.toHexString(0xFF & bytes[i]);
        if (hex.length() == 1){
            sb.append('0');
        }
        sb.append(hex);
    }
    return sb.toString();
}

將圖片的url轉(zhuǎn)成key后,就可以獲取Editor對象屈留。對于這個key來說局冰,如果當前不存在其他的Editor對象测蘑,那么editor就會返回一個新的Editor對象,通常它可以得到一個文件輸出流康二。

private final int DISK_CACHE_INDEX = 0;
String key = hashKeyFromUrl(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null) {
    OutputStream os = editor.newOutputStream(DISK_CACHE_INDEX);
}

注意:上面設置DISK_CACHE_INDEX為0碳胳,因為前面在DiskLruCache的open方法中設置了一個節(jié)點只能有一個數(shù)據(jù)。

存在輸出流后沫勿,當網(wǎng)絡下載圖片時挨约,圖片就可以通過這個文件輸出流寫入到系統(tǒng)文件中。

/**
* 實現(xiàn)根據(jù)傳入的urlString從網(wǎng)絡中下載圖片數(shù)據(jù)产雹,然后寫入輸出流os中
*/
public boolean downloadUrlToStream(String urlString, OutputStream os){
    //定義連接和輸入輸出流
    HttpURLConnection urlConnection = null;
    BufferedOutputStream out = null;
    BufferedInputStream in = null;
    try{
        final URL url = new URL(urlString);
        //獲取到連接和輸入輸出流
        urlConnection = (HttpURLConnection) url.openConnection();
        in = new BufferedInputStream(urlConnection.getInputStream(),
            IO_BUFFER_SIZE)诫惭;
        out = new BufferedOutputStream(os, IO_BUFFER_SIZE);
        //進行將網(wǎng)絡數(shù)據(jù)寫入os中
        int b = -1;
        while ((b = in.read()) != -1){
            out.write(b);
        }
        return true;
    } catch (IOException e){
        Log.d(TAG, "downloadBitmap failed." + e);
    } finally {
        //最后關(guān)閉連接和流
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
        MyUtils.close(out);
        MyUtils.close(in);
    }
    return false;
}

經(jīng)過上面的步驟,并沒有真正地將圖片寫入文件系統(tǒng)中蔓挖,還必須通過Editor的commit()來提交寫入操作夕土,如果圖片下載過程中出現(xiàn)異常,那么還可以通過Editor的abort()來回退整個操作瘟判。

OutputStream os = editor.newOutputStream(DISK_CACHE_INDEX);
//若寫入成功怨绣,則提交操作
if (downloadUrlToStream(url, os)) {
    editor.commit();
} 
//否則終止操作
else {
    editor.abort();
}
//最后刷新緩存區(qū),將緩存區(qū)中的文件寫入到系統(tǒng)中
mDiskLruCache.flush();
DiskLruCache的緩存查找
  • 將url轉(zhuǎn)換為key
  • 通過DiskLruCache的get方法得到一個Snapshot對象
  • 通過Snapshot對象獲取到緩存的文件輸入流
  • 通過輸入流拷获,獲取到Bitmap對象

為了避免加載圖片過程中導致的OOM問題篮撑,一般不建議直接加載原始圖片,而是通過BitmapFactory.Options對象來加載一張縮放后的圖片匆瓜,但是這種方法對FileInputStream的縮放存在問題咽扇,因為FileInputStream是一種有序的文件流,而兩次decodeStream調(diào)用影響了文件流的位置屬性陕壹,導致了第二次decodeStream時得到的是null质欲。為了解決該問題,可以通過文件流來得到它所對應的文件描述符糠馆,然后再通過BitmapFactory.decodeFileDescriptor方法來加載一張縮放后的圖片嘶伟。

//定義bitmap對象
Bitmap bitmap = null;
//將url轉(zhuǎn)換為key
String key = hashKeyFromUrl(url);
//根據(jù)key獲取到SnapShot對象
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
//若Snapshot對象不為空,說明存在該緩存
if (snapShot != null){
    //獲取到輸入流
    FileInputStream fis = (FileInputStream) snapShot.getInputStream(DISK_CACHE_INDEX);
    //根據(jù)輸入流獲取到文件描述符
    FileDescriptor fileDescriptor = fis.getFD();
    //調(diào)用壓縮圖片的方法又碌,獲取到bitmap對象
    bitmap = mImageResizer.decodeSampleBitmapFromFileDescriptor(fileDescriptor,
                reqWidth, reqHeight);
    //然后將bitmap對象加入到內(nèi)存緩存中
    if (bitmap != null){
        addBitmapToMemoryCache(key, bitmap);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末九昧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子毕匀,更是在濱河造成了極大的恐慌铸鹰,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皂岔,死亡現(xiàn)場離奇詭異蹋笼,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門剖毯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來圾笨,“玉大人,你說我怎么就攤上這事逊谋±薮铮” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵胶滋,是天一觀的道長板鬓。 經(jīng)常有香客問我,道長究恤,這世上最難降的妖魔是什么穗熬? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮丁溅,結(jié)果婚禮上唤蔗,老公的妹妹穿的比我還像新娘。我一直安慰自己窟赏,他們只是感情好妓柜,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涯穷,像睡著了一般棍掐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拷况,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天作煌,我揣著相機與錄音,去河邊找鬼赚瘦。 笑死粟誓,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的起意。 我是一名探鬼主播鹰服,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼揽咕!你這毒婦竟也來了悲酷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤亲善,失蹤者是張志新(化名)和其女友劉穎设易,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛹头,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡顿肺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年戏溺,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挟冠。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖袍睡,靈堂內(nèi)的尸體忽然破棺而出知染,到底是詐尸還是另有隱情,我是刑警寧澤斑胜,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布控淡,位于F島的核電站,受9級特大地震影響止潘,放射性物質(zhì)發(fā)生泄漏掺炭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一凭戴、第九天 我趴在偏房一處隱蔽的房頂上張望涧狮。 院中可真熱鬧,春花似錦么夫、人聲如沸者冤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涉枫。三九已至,卻和暖如春腐螟,著一層夾襖步出監(jiān)牢的瞬間愿汰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工乐纸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留衬廷,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓汽绢,卻偏偏與公主長得像泵督,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子庶喜,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容