Android中的緩存

為什么會用到緩存呢充尉?主要是流量耗不起啊,國內(nèi)的公共場所的WiFi的普及率不高衣形,因此必須考慮流量的問題驼侠,說白了,就是用戶體驗啊谆吴,每次都網(wǎng)絡(luò)請求倒源,消耗資源不說,網(wǎng)速不好的情況下還會有網(wǎng)絡(luò)延時句狼,用戶體驗不好笋熬。

Android中的緩存,從方式上來說腻菇,一般有網(wǎng)絡(luò)緩存突诬,磁盤緩存即SD卡緩存,內(nèi)存緩存芜繁。網(wǎng)絡(luò)緩存需要服務端的配合旺隙,用于加快網(wǎng)絡(luò)請求的響應速度。磁盤緩存一般用DiskLruCache骏令,當然也可以用SqlLite數(shù)據(jù)庫蔬捷,以及sharedpreference等作持久化處理。這里主要說下兩種常用的緩存方法,LruCache周拐、DiskLruCache铡俐。前者用于內(nèi)存緩存,后者用于設(shè)備緩存妥粟,一般兩者結(jié)合起來效果更好审丘。

其實緩存的實現(xiàn)并不難,每一中緩存都會有三個基本操作勾给,添加滩报、獲取、刪除播急。了解這些了脓钾,就會有思路了。

再說LruCache桩警、DiskLruCache可训,可以看到,兩者都有Lru捶枢,那么Lru是什么呢握截?這是目前常用的一種緩存算法:近期最少使用算法,核心思想很簡單烂叔,就是當緩存滿時川蒙,會優(yōu)先刪除那些近期最少使用的緩存。那么現(xiàn)在分別了解下這兩種緩存吧长已。

LruCache

LruCache內(nèi)部用到的是LinkedHashMap畜眨,LinkedHashMap與HashMap的不同住處在于LinkedHashMap 維護著一個運行于所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序术瓮,該迭代順序可以是插入順序或者是訪問順序康聂。也就說它的插入和訪問是有順序的。另外LruCache是線程安全的胞四。至于使用的話就很簡單了恬汁。

 // 初始化
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    int cacheSize = maxMemory / 8;
    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap value) {
            return value.getRowBytes() * value.getHeight() / 1024;
        }
    };

總緩存大小一般會設(shè)置為當前進程可用內(nèi)存的1/8,當然這個數(shù)是可以自己設(shè)置的辜伟,這個數(shù)是推薦的氓侧。sizeOf方法是為了計算緩存對象的大小。如果有必要也可以重寫entryRemoved來完成某些資源回收工作导狡。

再看緩存的添加與刪除约巷,

    //添加緩存
    mMemoryCache.put(key,bitmap);
    //獲取緩存
    mMemoryCache.get(key);
    //刪除緩存
    mMemoryCache.remove(key);    
DiskLruCache

DiskLruCache用與磁盤緩存,被官方推薦使用旱捧。下面來看看它的使用独郎。

自從用了Gradle后踩麦,引入項目方便多了,誰用誰知道氓癌。

compile 'com.jakewharton:disklrucache:2.0.2'

創(chuàng)建DiskLruCache:

DiskLruCache mDiskLruCache = null;  
try {  
    File cacheDir = getDiskCacheDir(context, "bitmap");  
    if (!cacheDir.exists()) {  
        cacheDir.mkdirs();  
    }  
    mDiskLruCache = DiskLruCache.open(cacheDir, 1, 1, 10 * 1024 * 1024);  
} catch (IOException e) {  
    e.printStackTrace();  
}  ```
        
解釋下DiskLruCache.open的參數(shù)谓谦,第一個表示存儲的路徑,第二個表示應用的版本號贪婉,注意這里當版本號發(fā)生改變時會清空之前所有的緩存文件反粥,而在實際開發(fā)中這個性質(zhì)用的不多,所以直接寫1疲迂。第三個表示單個節(jié)點對應的數(shù)據(jù)的個數(shù)才顿,設(shè)置為1就可以了,第四個表示緩存的總大小鬼譬,當超出這個值時娜膘,會清除一些緩存保證總大小不大于這個設(shè)定的值逊脯。

添加緩存:
第一步优质,網(wǎng)絡(luò)下載圖片(文件也是一樣的步驟的)并通過outputStream寫入到本地

    private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {  
    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(), 8 * 1024);  
        out = new BufferedOutputStream(outputStream, 8 * 1024);  
        int b;  
        while ((b = in.read()) != -1) {  
            out.write(b);  
        }  
        return true;  
    } catch (final IOException e) {  
        e.printStackTrace();  
    } finally {  
        if (urlConnection != null) {  
            urlConnection.disconnect();  
        }  
        try {  
            if (out != null) {  
                out.close();  
            }  
            if (in != null) {  
                in.close();  
            }  
        } catch (final IOException e) {  
            e.printStackTrace();  
        }  
    }  
    return false;  
    }  
第二步,處理緩存的key军洼,直接用url作為key值時最有快捷的方式巩螃,但是url里會有特殊字符,不符合Android的命名規(guī)范匕争,最好的辦法就是把url進行MD5摘要避乏。

public String hashKeyForDisk(String key) {
String cacheKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(key.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();
} ```

第三步 創(chuàng)建DiskLruCache.Editor的實例,寫入數(shù)據(jù)

            String key = hashKeyForDisk(imageUrl);  
            DiskLruCache.Editor editor = mDiskLruCache.edit(key);  
            if (editor != null) {  
                OutputStream outputStream = editor.newOutputStream(0);  
                if (downloadUrlToStream(imageUrl, outputStream)) {  
                    editor.commit();  
                } else {  
                    editor.abort();  
                }  
            }  
            mDiskLruCache.flush();  ```

editor.commit()方法用來提交寫入操作甘桑,editor.abort()回退整個操作拍皮。

讀取緩存:
    Bitmap bitmap = null;
    String key = hashKeyFormUrl(url);
    DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
    if (snapShot != null) {
        FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(0);
        FileDescriptor fileDescriptor = fileInputStream.getFD();
        bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor,
                reqWidth, reqHeight);
        if (bitmap != null) {
            addBitmapToMemoryCache(key, bitmap);
        }
    }  ```
public Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight) {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fd, null, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth,
                reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFileDescriptor(fd, null, options);
    }

需要說明下的是為了避免加載圖片時導致OOM,不建議直接加在Bitmap跑杭,通常我們會通過BitmapFactory.Options來加載一張縮放的圖片铆帽,但是這中方法對于FileInputStream有問題,因為FileInputStream是有序的文件流德谅,而兩次的從的 decodeStream調(diào)用影響了文件流的位置屬性爹橱,導致第二次decodeStream時得到的為null。為了解決這個問題窄做,可以先得到對應的文件描述符愧驱,然后通過BitmapFactory.decodeFileDescriptor()來加載圖片。
移除緩存:

mDiskLruCache.remove(key); 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末椭盏,一起剝皮案震驚了整個濱河市组砚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掏颊,老刑警劉巖惫确,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡改化,警方通過查閱死者的電腦和手機掩蛤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陈肛,“玉大人揍鸟,你說我怎么就攤上這事【浜担” “怎么了阳藻?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谈撒。 經(jīng)常有香客問我腥泥,道長,這世上最難降的妖魔是什么啃匿? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任蛔外,我火速辦了婚禮,結(jié)果婚禮上溯乒,老公的妹妹穿的比我還像新娘夹厌。我一直安慰自己,他們只是感情好裆悄,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布矛纹。 她就那樣靜靜地躺著,像睡著了一般光稼。 火紅的嫁衣襯著肌膚如雪或南。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天艾君,我揣著相機與錄音采够,去河邊找鬼。 笑死腻贰,一個胖子當著我的面吹牛吁恍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播播演,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼冀瓦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了写烤?” 一聲冷哼從身側(cè)響起翼闽,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洲炊,沒想到半個月后感局,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尼啡,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年询微,在試婚紗的時候發(fā)現(xiàn)自己被綠了崖瞭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡撑毛,死狀恐怖书聚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情藻雌,我是刑警寧澤雌续,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站胯杭,受9級特大地震影響驯杜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜做个,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一鸽心、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叁温,春花似錦再悼、人聲如沸核畴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谤草。三九已至跟束,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丑孩,已是汗流浹背冀宴。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留温学,地道東北人略贮。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像仗岖,于是被迫代替她去往敵國和親逃延。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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