LruCache 的使用總結(jié)

https://developer.android.google.cn/topic/performance/graphics/cache-bitmap.html
https://developer.android.google.cn/reference/android/util/LruCache.html

LRU (Least Recently Used) 就是最近最少使用算法,LruCache當(dāng)然就是依據(jù) LRU 算法實(shí)現(xiàn)的緩存婆廊。簡單說就是迅细,設(shè)置好緩存大小淘邻;當(dāng)緩存空間不足的時候茵典,就把最近最少使用(也就是最長時間沒有使用)的緩存項(xiàng)清除掉;然后提供新的緩存宾舅。
LruCache 的使用其實(shí)比較簡單统阿,可以歸結(jié)為以下要點(diǎn):

  • LruCache 內(nèi)部使用 LinkedHashMap 實(shí)現(xiàn),所以 LruCache 保存的是鍵值對
  • LruCache 本身對緩存項(xiàng)是強(qiáng)引用
  • LruCache 的讀寫是線程安全的筹我,內(nèi)部加了 synchronized扶平。也就是 put(K key, V value) 和 get(K key) 內(nèi)部有 synchronized
  • key 和 value 不接受 null 。所以如果 get 到了 null 蔬蕊,那就說明是沒有緩存
  • Override sizeOf(K key, V value) 方法
  • 根據(jù)需要Override entryRemoved(boolean evicted, K key, V oldValue, V newValue) 和 create(K key) 方法

感覺已經(jīng)寫完了结澄。

好吧,還是從實(shí)際使用的角度展開一下吧岸夯。

  1. 從構(gòu)造函數(shù)就能看出來麻献,LruCache 內(nèi)部使用 LinkedHashMap 實(shí)現(xiàn)。在《JAVA 核心技術(shù) 卷1》里面介紹 LinkedHashMap 的時候猜扮,就提到過 LinkedHashMap 可以用來做 LRU 算法勉吻。

     public LruCache(int maxSize) {
         if (maxSize <= 0) {
             throw new IllegalArgumentException("maxSize <= 0");
         }
         this.maxSize = maxSize;
         this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
     }
    

    并且,在構(gòu)造的時候就傳入了 int maxSize 旅赢,也就是緩存大小齿桃。maxSize 的具體含義,或者說單位鲜漩,是和 int sizeOf(K key, V value) 的返回值對應(yīng)的源譬。
    LruCache 也提供了 resize(int maxSize) 方法重新設(shè)置緩存大小。

  2. int sizeOf(K key, V value) 用來計算每一個緩存項(xiàng)的大小孕似〔饶铮或者,簡單點(diǎn)說喉祭,LruCache 是根據(jù)對每一個緩存項(xiàng)調(diào)用的 sizeOf 的返回值累加养渴,判斷當(dāng)前緩存空間是否超出 maxSize。
    所以泛烙,實(shí)際使用 LruCache 的時候理卑,我們可能看到這樣的代碼:

    int cacheSize = 4 * 1024 * 1024; // 4MiB
    LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
        protected int sizeOf(String key, Bitmap value) {
            return value.getByteCount();
        }
    }
    

    當(dāng)然,也可以不覆寫 sizeOf 方法蔽氨。這個方法默認(rèn)返回1藐唠。所以帆疟,比如說我很明確這里是要緩存32個圖片縮略圖,不是很在乎每個縮略圖具體的內(nèi)存大小宇立,就可以直接設(shè)置 maxSize 為32踪宠;而 sizeOf 默認(rèn)返回1就可以。

  3. 緩存的讀寫妈嘹,簡單點(diǎn)說就是 put(K key, V value) 和 V get(K key)柳琢。
    不過,我覺得LruCache的代碼寫的不錯润脸,所以貼一下吧,學(xué)習(xí)學(xué)習(xí)毙驯。

    在put方法中可以看到倒堕,

    • 首先,key 和 value都不可以為 null尔苦。
    • 其次涩馆,如果(previous != null),說明該key之前對應(yīng)了一個value允坚,這時會調(diào)用 entryRemoved() 方法魂那。這默認(rèn)是一個空方法,我們可以根據(jù)需要 Override稠项。
    • 還有就是涯雅,方法最后調(diào)用了 trimToSize(int maxSize) 方法,顧名思義展运,就是用來修剪緩存大小活逆,使之小于 maxSize。也就是刪除掉最近最少使用的緩存對象拗胜,直到 實(shí)際緩存 <= maxSize蔗候。trimToSize 方法在 while 循環(huán)內(nèi)加了 synchronized (this) 來實(shí)現(xiàn)刪除,代碼很簡單埂软。
     public final V put(K key, V value) {
         if (key == null || value == null) {
             throw new NullPointerException("key == null || value == null");
         }
    
         V previous;
         synchronized (this) {
             putCount++;
             size += safeSizeOf(key, value);
             previous = map.put(key, value);
             if (previous != null) {
                 size -= safeSizeOf(key, previous);
             }
         }
    
         if (previous != null) {
             entryRemoved(false, key, previous, value);
         }
    
         trimToSize(maxSize);
         return previous;
     }
    

    get 方法寫的也很漂亮锈遥。

    • 如果能夠從 map 中拿到緩存值,就直接 return勘畔。
    • 不然的話所灸,就會調(diào)用 create 方法去創(chuàng)建一個。所以炫七,我們可以根據(jù)需要去覆寫 create 方法爬立。這個方法默認(rèn)返回 null。
    • 并且万哪,多線程的情況下侠驯,可能存在第一步 get 為 null抡秆,調(diào)用 create 方法后,其他線程已經(jīng) put 了 key 的 value 的情況吟策,所以就會把 被 create 方法的值覆蓋的 mapValue 重新寫回去琅轧。也就是有注釋“// There was a conflict so undo that last put”的情況。
    // 真心覺得 Google 代碼寫得漂亮踊挠。
    
     public final V get(K key) {
         if (key == null) {
             throw new NullPointerException("key == null");
         }
    
         V mapValue;
         synchronized (this) {
             mapValue = map.get(key);
             if (mapValue != null) {
                 hitCount++;
                 return mapValue;
             }
             missCount++;
         }
    
         /*
          * Attempt to create a value. This may take a long time, and the map
          * may be different when create() returns. If a conflicting value was
          * added to the map while create() was working, we leave that value in
          * the map and release the created value.
          */
    
         V createdValue = create(key);
         if (createdValue == null) {
             return null;
         }
    
         synchronized (this) {
             createCount++;
             mapValue = map.put(key, createdValue);
    
             if (mapValue != null) {
                 // There was a conflict so undo that last put
                 map.put(key, mapValue);
             } else {
                 size += safeSizeOf(key, createdValue);
             }
         }
    
         if (mapValue != null) {
             entryRemoved(false, key, createdValue, mapValue);
             return mapValue;
         } else {
             trimToSize(maxSize);
             return createdValue;
         }
     }
    
  4. 從上面的分析也能看出,create 方法是在緩存 map 中沒有 key 對應(yīng)的 value 的情況下冲杀,構(gòu)造一個默認(rèn)值效床,并保存到緩存中。所以我們可以根據(jù)需要权谁,選擇是否覆寫這個方法剩檀。
    entryRemoved 方法,是在 map 中有 value 被覆蓋旺芽,或者被刪除(trimToSize 方法中會刪除)的時候調(diào)用沪猴,我們也可以根據(jù)需要覆寫這個方法。

所以采章,LruCache 的使用真的很簡單运嗜,在不覆寫 sizeOf 方法的情況下,可以直接 new 一個悯舟,并指定 maxSize担租,然后 put 、get 就可以了抵怎。


LruCache 的源碼中奋救,還有一個地方我覺得寫得比較好。
那就是反惕,上面的代碼中都是調(diào)用的 safeSizeOf 方法尝艘,而不是 sizeOf 方法。
而我一直在說姿染,我們可以根據(jù)需要覆寫 sizeOf 方法背亥,這是為什么呢?

看看源碼:

    private int safeSizeOf(K key, V value) {
        int result = sizeOf(key, value);
        if (result < 0) {
            throw new IllegalStateException("Negative size: " + key + "=" + value);
        }
        return result;
    }

相比 sizeOf 方法盔粹,safeSizeOf 雖然最終也是調(diào)用的 sizeOf隘梨,但是增加了
異常判斷。
這樣的好處是舷嗡, sizeOf 可以開放給最終的 API 使用者直接去覆寫轴猎;而代碼中,又不必到處都是對 sizeOf 的返回值的異常判斷进萄。

所以捻脖,LruCache 的整個代碼锐峭,我都覺得寫得很漂亮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末可婶,一起剝皮案震驚了整個濱河市沿癞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌矛渴,老刑警劉巖椎扬,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異具温,居然都是意外死亡蚕涤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門铣猩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揖铜,“玉大人,你說我怎么就攤上這事达皿√煜牛” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵峦椰,是天一觀的道長龄寞。 經(jīng)常有香客問我,道長们何,這世上最難降的妖魔是什么萄焦? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮冤竹,結(jié)果婚禮上拂封,老公的妹妹穿的比我還像新娘。我一直安慰自己鹦蠕,他們只是感情好冒签,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钟病,像睡著了一般萧恕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肠阱,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天票唆,我揣著相機(jī)與錄音,去河邊找鬼屹徘。 笑死走趋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的噪伊。 我是一名探鬼主播簿煌,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼氮唯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了姨伟?” 一聲冷哼從身側(cè)響起惩琉,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎夺荒,沒想到半個月后瞒渠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡技扼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年在孝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淮摔。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖始赎,靈堂內(nèi)的尸體忽然破棺而出和橙,到底是詐尸還是另有隱情,我是刑警寧澤造垛,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布魔招,位于F島的核電站,受9級特大地震影響五辽,放射性物質(zhì)發(fā)生泄漏办斑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一杆逗、第九天 我趴在偏房一處隱蔽的房頂上張望乡翅。 院中可真熱鬧,春花似錦罪郊、人聲如沸蠕蚜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靶累。三九已至,卻和暖如春癣疟,著一層夾襖步出監(jiān)牢的瞬間挣柬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工睛挚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留邪蛔,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓竞川,卻偏偏與公主長得像店溢,于是被迫代替她去往敵國和親叁熔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

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

  • 非原創(chuàng)床牧,尊重作者荣回!官方鏈接:http://www.reibang.com/p/b49a111147ee 關(guān)于And...
    小小程序員jh閱讀 1,841評論 0 16
  • 為了減少我們從網(wǎng)絡(luò)獲取數(shù)據(jù)(圖片)的次數(shù),我們會從網(wǎng)絡(luò)獲取到之后戈咳,緩存到我們的內(nèi)存中心软。所以在我們網(wǎng)絡(luò)編程中,緩存技...
    馮奕歡閱讀 999評論 0 0
  • LruCache LruCache是Android3.1提供的緩存類著蛙,并且在v4包提供了該類删铃。LruCache是一...
    Cris_Ma閱讀 2,998評論 0 1
  • 1 餓漢式(靜態(tài)常量)[可用]優(yōu)點(diǎn):這種寫法比較簡單,就是在類裝載的時候就完成實(shí)例化踏堡。避免了線程同步問題猎唁。缺點(diǎn):在...
    待汝豪杰只是凡夫閱讀 227評論 0 0
  • 有一句話叫做,沒有公主命顷蟆,就要有女王心诫隅。 讀書時代的我,做自己的女王帐偎,心氣很高逐纬,事事爭強(qiáng)。男生們對我的評價是削樊,孤高...
    CraZY_Emily閱讀 170評論 0 0