傻瓜式的帶你深入了解glide的內(nèi)存機制

真正的傻瓜式,人人都看得懂

glide一共有幾級緩存

三級緩存大莫,activeResource,內(nèi)存緩存,硬盤緩存
有代碼為證:class Engine

     //先從activeResource加載
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      //有結(jié)果就直接返回
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return null;
    }

     //從內(nèi)存緩存中加載
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return null;
    }

   //都沒有挎袜,到異步線程去執(zhí)行邏輯,包括硬盤緩存跟請求網(wǎng)絡(luò)
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb, callbackExecutor);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }

activeResource

比如如下的頁面肥惭,兩個imageview加載同樣的一張圖片盯仪,在內(nèi)存中,其實只有一份bitmap蜜葱,這個就是activeResource的功勞


image.png
image.png

通過glide加載的圖片資源全景,多個地方加載同個資源,都是返回同一份bitmap牵囤,避免bitmap重復(fù)(當(dāng)頁面銷毀爸黄,資源也會從activeResource中移除)

ActiveResources代碼參考

//加入圖片的引用,內(nèi)部是一個hashmap的弱引用在管理
synchronized void activate(Key key, EngineResource<?> resource) {
    ResourceWeakReference toPut =
        new ResourceWeakReference(
            key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);

    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
    if (removed != null) {
      removed.reset();
    }
  }

//移除引用
  synchronized void deactivate(Key key) {
    ResourceWeakReference removed = activeEngineResources.remove(key);
    if (removed != null) {
      removed.reset();
    }
  }

//查詢是否有某個圖片的引用
  @Nullable
  synchronized EngineResource<?> get(Key key) {
    ResourceWeakReference activeRef = activeEngineResources.get(key);
    if (activeRef == null) {
      return null;
    }

    EngineResource<?> active = activeRef.get();
    if (active == null) {
      cleanupActiveReference(activeRef);
    }
    return active;
  }

所以揭鳞,內(nèi)部的資源加載(各種R.drawable)炕贵,圖片加載,盡量使用glide來加載汹桦,是最好的選擇

那有個問題來了鲁驶,如果加載兩張一樣的圖片,但是imageview的寬高不一樣舞骆,還是內(nèi)存中只有一份bitmap嗎钥弯?
我們直接驗證看下


image.png

image.png

可以看到,內(nèi)存有兩張bitmap督禽,并且兩張bitmap的尺寸不一樣

這里就涉及到上面保存引用的key的邏輯了脆霎,可以直接看代碼

class EngineKeyFactory {

  @SuppressWarnings("rawtypes")
  EngineKey buildKey(Object model, Key signature, int width, int height,
      Map<Class<?>, Transformation<?>> transformations, Class<?> resourceClass,
      Class<?> transcodeClass, Options options) {
    return new EngineKey(model, signature, width, height, transformations, resourceClass,
        transcodeClass, options);
  }
}

可以看到,key跟很多參數(shù)相關(guān)
model:就是加載的url
signature:額外的簽名
width狈惫,height:imageview的寬高
transformations:就是轉(zhuǎn)換睛蛛,比如centerCrop鹦马,roundCorner等

由于我們這里,只是寬高不一樣忆肾,所以為了保證同一個key荸频,保證寬高是一樣的就好,可以使用override方式
Glide.with(this).load(url).override(Target.SIZE_ORIGINAL).into(imageView)

這樣key是一樣客冈,在內(nèi)存中也就只有一份bitmap了旭从,提升了App性能

內(nèi)存緩存

內(nèi)存緩存的大小
有一套復(fù)雜的計算規(guī)則,不過大多數(shù)手機场仲,都是屏幕寬高*8和悦,常規(guī)的手機就是17M左右
詳細的計算規(guī)則可以查看類MemorySizeCalculator
對于大多數(shù)App,這個內(nèi)存是偏小的渠缕,通過直接放大1.5倍鸽素,代碼如下
Glide.get(context).setMemoryCategory(MemoryCategory.HIGH);
如果還是不夠的話,需要通過AppGlideModule來實現(xiàn)

內(nèi)存緩存內(nèi)部是采用LinkedHashMap來實現(xiàn)的亦鳞,最新加的馍忽,放在最后面,緩存總大小到了上限值燕差,越早加入緩存的舵匾,越先被清空,可以看下構(gòu)造函數(shù)

/**
     * Constructs an empty <tt>LinkedHashMap</tt> instance with the
     * specified initial capacity, load factor and ordering mode.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @param  accessOrder     the ordering mode - <tt>true</tt> for
     *         access-order, <tt>false</tt> for insertion-order
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

關(guān)鍵是這個accessOrder谁不,需要設(shè)置為true坐梯,這樣就會按照范圍的順序排序,最近被訪問或者使用的刹帕,就越不會被回收

獲取一個內(nèi)存緩存吵血,很簡單,就是從hashmap的get方法就可以

/**
   * Returns the item in the cache for the given key or null if no such item exists.
   *
   * @param key The key to check.
   */
  @Nullable
  public synchronized Y get(@NonNull T key) {
    return cache.get(key);
  }

放入一個圖片到緩存中

/**
   * Adds the given item to the cache with the given key and returns any previous entry for the
   * given key that may have already been in the cache.
   *
   * <p>If the size of the item is larger than the total cache size, the item will not be added to
   * the cache and instead {@link #onItemEvicted(Object, Object)} will be called synchronously with
   * the given key and item.
   *
   * @param key  The key to add the item at.
   * @param item The item to add.
   */
  @Nullable
  public synchronized Y put(@NonNull T key, @Nullable Y item) {
    final int itemSize = getSize(item);
    if (itemSize >= maxSize) {
      onItemEvicted(key, item);
      return null;
    }

    if (item != null) {
      currentSize += itemSize;
    }
    @Nullable final Y old = cache.put(key, item);
    if (old != null) {
      currentSize -= getSize(old);

      if (!old.equals(item)) {
        onItemEvicted(key, old);
      }
    }
    evict();

    return old;
  }

放入內(nèi)存緩存的時機是什么時候偷溺?蹋辅?
頁面銷毀的時候,證據(jù)如下


image.png

邏輯是這樣
加載到一張圖片挫掏,顯示在view上侦另,同時放入activeResource緩存,單view銷毀了尉共,從activeResource緩存中挪到memoryCache中
下次再打開頁面褒傅,加載同一種圖片,從memoryCache中袄友,remove掉該圖片殿托,加載到view里面,同時放入activeResource中

所以放入內(nèi)存緩存中的圖片剧蚣,都是不在頁面顯示的圖片

硬盤緩存

硬盤緩存的大小和默認緩存路徑

大兄е瘛:250M
路徑:/data/user/0/com.example.myapplication/cache/image_manager_disk_cache

interface Factory {
    /** 250 MB of cache. */
    int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;
    String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache";
    /** Returns a new disk cache, or {@code null} if no disk cache could be created. */
    @Nullable
    DiskCache build();
  }

問題:正寫入一個緩存文件到硬盤旋廷,App崩潰了,怎么辦礼搁?
答案:日志文件
一個日志文件饶碘,內(nèi)容大概這樣

     *     libcore.io.DiskLruCache
     *     1
     *     100
     *     1
     *
     *     CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
     *     DIRTY 335c4c6028171cfddfbaae1a9c313c52
     *     CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
     *     REMOVE 335c4c6028171cfddfbaae1a9c313c52
     *     DIRTY 1ab96a171faeeee38496d8b330771a7a
     *     CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
     *     READ 335c4c6028171cfddfbaae1a9c313c52
     *     READ 3400330d1dfc7f3f7f4b8d4d803dfcf6

寫入一個緩存的邏輯是這樣
現(xiàn)在日志文件,寫一行DIRTY的日志
寫入真實的緩存的圖片
在日志文件馒吴,寫入一行CLEAN的日志

這樣熊镣,當(dāng)寫入的圖片失敗,或者App崩潰募书,日志里面,就只有DIRTH記錄测蹲,沒有CLEAN記錄莹捡,就可以把這次DIRTH的緩存給刪掉

加載一個緩存文件
通過日志文件,會生成一個硬盤緩存的hashmap扣甲,通過key索引篮赢,可以加載到這個拿到這個hashmap里面的值,可以實際拿到文件的緩存路徑

/**
   * Returns a snapshot of the entry named {@code key}, or null if it doesn't
   * exist is not currently readable. If a value is returned, it is moved to
   * the head of the LRU queue.
   */
  public synchronized Value get(String key) throws IOException {
    checkNotClosed();
    Entry entry = lruEntries.get(key);
    if (entry == null) {
      return null;
    }

    if (!entry.readable) {
      return null;
    }

    for (File file : entry.cleanFiles) {
        // A file must have been deleted manually!
        if (!file.exists()) {
            return null;
        }
    }

    redundantOpCount++;
    journalWriter.append(READ);
    journalWriter.append(' ');
    journalWriter.append(key);
    journalWriter.append('\n');
    if (journalRebuildRequired()) {
      executorService.submit(cleanupCallable);
    }

    return new Value(key, entry.sequenceNumber, entry.cleanFiles, entry.lengths);
  }

刪除一個緩存
先刪除文件琉挖,然后往日志里面寫入REMOVE的標(biāo)記

public synchronized boolean remove(String key) throws IOException {
    checkNotClosed();
    Entry entry = lruEntries.get(key);
    if (entry == null || entry.currentEditor != null) {
      return false;
    }

    for (int i = 0; i < valueCount; i++) {
      File file = entry.getCleanFile(i);
      if (file.exists() && !file.delete()) {
        throw new IOException("failed to delete " + file);
      }
      size -= entry.lengths[i];
      entry.lengths[i] = 0;
    }

    redundantOpCount++;
    journalWriter.append(REMOVE);
    journalWriter.append(' ');
    journalWriter.append(key);
    journalWriter.append('\n');

    lruEntries.remove(key);

    if (journalRebuildRequired()) {
      executorService.submit(cleanupCallable);
    }

    return true;
  }

刪除所有的硬盤緩存

public void delete() throws IOException {
    close();
    Util.deleteContents(directory);
  }

日志文件路徑:
/data/user/0/com.example.myapplication/cache/image_manager_disk_cache/journal
緩存文件路徑:
/data/user/0/com.example.myapplication/cache/image_manager_disk_cache/6da652fb74c5ae6035ec3f7cab315b6f5dedb11515c173b2097d02a14c7974b9.0

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末启泣,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子示辈,更是在濱河造成了極大的恐慌寥茫,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矾麻,死亡現(xiàn)場離奇詭異纱耻,居然都是意外死亡,警方通過查閱死者的電腦和手機险耀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門弄喘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人甩牺,你說我怎么就攤上這事蘑志。” “怎么了贬派?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵急但,是天一觀的道長。 經(jīng)常有香客問我搞乏,道長羊始,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任查描,我火速辦了婚禮突委,結(jié)果婚禮上柏卤,老公的妹妹穿的比我還像新娘。我一直安慰自己匀油,他們只是感情好缘缚,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著敌蚜,像睡著了一般桥滨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弛车,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天齐媒,我揣著相機與錄音,去河邊找鬼纷跛。 笑死喻括,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的贫奠。 我是一名探鬼主播唬血,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼唤崭!你這毒婦竟也來了拷恨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤谢肾,失蹤者是張志新(化名)和其女友劉穎腕侄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芦疏,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡兜挨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了眯分。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拌汇。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖弊决,靈堂內(nèi)的尸體忽然破棺而出噪舀,到底是詐尸還是另有隱情,我是刑警寧澤飘诗,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布与倡,位于F島的核電站,受9級特大地震影響昆稿,放射性物質(zhì)發(fā)生泄漏纺座。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一溉潭、第九天 我趴在偏房一處隱蔽的房頂上張望净响。 院中可真熱鬧少欺,春花似錦、人聲如沸馋贤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽配乓。三九已至仿滔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間犹芹,已是汗流浹背崎页。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腰埂,地道東北人飒焦。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像盐固,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丈挟,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

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

  • 7.1 壓縮圖片 一刁卜、基礎(chǔ)知識 1、圖片的格式 jpg:最常見的圖片格式曙咽。色彩還原度比較好蛔趴,可以支持適當(dāng)壓縮后保持...
    AndroidMaster閱讀 2,487評論 0 13
  • 一、簡介 在泰國舉行的谷歌開發(fā)者論壇上例朱,谷歌為我們介紹了一個名叫Glide的圖片加載庫孝情,作者是bumptech。這...
    天天大保建閱讀 7,453評論 2 28
  • Glide緩存簡介 Glide的緩存分成了兩個模塊洒嗤,一個是內(nèi)存緩存箫荡,一個是硬盤緩存。 這兩個緩存模塊的作用各不相同...
    7196ba90d394閱讀 975評論 0 1
  • 暑期社會實踐的第一天渔隶,我們微愛暖社會實踐隊一行十余人來到了鄭州市紫荊山公園準(zhǔn)備幫助環(huán)衛(wèi)工人打掃衛(wèi)生羔挡,順便對街道的衛(wèi)...
    水淺則無魚閱讀 171評論 0 0
  • 通過在網(wǎng)路中的查找,得知《氓》創(chuàng)作于我國的西周间唉。因為我剛看過一篇關(guān)於中國古代婚姻家庭制度的文章绞灼,加之這首愛情詩的創(chuàng)...
    艱苦卓絕1204閱讀 542評論 0 2