Android Glide 緩存機制及源碼

      網友投稿 1206 2022-05-28

      Glide的簡單使用

      Glide里的緩存

      默認情況下,Glide 會在開始一個新的圖片請求之前檢查以下多級的緩存:

      活動資源 (Active Resources) - 現在是否有另一個 View 正在展示這張圖片?

      內存緩存 (Memory cache) - 該圖片是否最近被加載過并仍存在于內存中?

      資源類型(Resource) - 該圖片是否之前曾被解碼、轉換并寫入過磁盤緩存?

      數據來源 (Data) - 構建這個圖片的資源是否之前曾被寫入過文件緩存?

      前兩步檢查圖片是否在內存中,如果是則直接返回圖片。后兩步則檢查圖片是否在磁盤上,以便快速但異步地返回圖片。

      如果四個步驟都未能找到圖片,則Glide會返回到原始資源以取回數據(原始文件,Uri, Url等)。

      什么是三級緩存?

      內存緩存:優先加載,速度最快

      本地緩存:其次加載,速度快

      網絡緩存:最后加載,速度慢,浪費流量

      緩存機制

      Glide使用了ActiveResources(活動緩存弱引用)+MemoryCache(內存緩存Lru算法)+DiskCache(磁盤緩存Lru算法)。

      ActiveResources:存儲當前界面使用到的圖片。界面不展示后,該Bitmap又被緩存至MemoryCache中,并從ActiveResources中刪除。

      Memory Cache:存儲當前沒有使用到的Bitmap,當MemoryCache中得到Bitmap后,該Bitmap又被緩存至ActiveResources中,并從MemoryCache中刪除。

      Disk Cache:持久緩存。例如圖片加圓角,處理后圖片會被緩存到文件中,應用被再次打開時可以加載緩存直接使用。

      注意: ActiveResources + MemoryCache是內存緩存,都屬于運行時緩存,且互斥(同一張圖片不會同時緩存在ActiveResources+MemoryCache),應用被殺死后將不存在。

      Glide 內部是使用 LruCache、弱引用和硬盤緩存實現的。

      Glide 主要將緩存分為兩塊內存緩存和硬盤緩存,兩種緩存的結合,構成了 Glide 緩存機制的核心。

      為何設計出活動緩存

      因為內存緩存使用LRU算法,當你使用Gilde加載并顯示第一張圖片時,后面又加載了很多圖片,同時你的第一張圖片還在用。這個時候內存緩存根據LRU算法可能會刪除你正在使用的第一張照片。這樣的后果就是你正在使用的照片找不到,后果就是程序崩潰。

      加載流程

      流程就是這么個流程下面咱們通過源碼加深一下。

      Glide源碼

      加載流程

      1.Engine類

      負責啟動加載并管理活動資源和緩存資源,它里面有個load方法。沒錯就是提供路徑加載圖片的方法。

      2.load方法

      這個方法里面滿滿的干貨。

      public??LoadStatus?load(...)?{

      long?startTime?=?VERBOSE_IS_LOGGABLE???LogTime.getLogTime()?:?0;

      EngineKey?key?=

      keyFactory.buildKey(

      model,

      signature,

      width,

      height,

      transformations,

      resourceClass,

      transcodeClass,

      options);

      EngineResource?memoryResource;

      synchronized?(this)?{

      memoryResource?=?loadFromMemory(key,?isMemoryCacheable,?startTime);

      if?(memoryResource?==?null)?{

      return?waitForExistingOrStartNewJob(...);

      }

      }

      //?Avoid?calling?back?while?holding?the?engine?lock,?doing?so?makes?it?easier?for?callers?to

      //?deadlock.

      cb.onResourceReady(

      memoryResource,?DataSource.MEMORY_CACHE,?/*?isLoadedFromAlternateCacheKey=?*/?false);

      return?null;

      }

      3.EngineKey

      An in memory only cache key used to multiplex loads.

      用于多路傳輸加載的僅內存緩存密鑰.

      EngineKey?key?=

      keyFactory.buildKey(

      ...);

      4.loadFromMemory

      根據上面load方法提供咱們來看看loadFromMemory()這個是重點;

      5.loadFromActiveResources

      6.loadFromCache

      7.getEngineResourceFromCache

      到這里如有還未找到,那就說明該圖片未保存至內存緩存中來。咱繼續往下走,順著源碼跑。

      8.waitForExistingOrStartNewJob

      咱弄個簡化版

      private??LoadStatus?waitForExistingOrStartNewJob(...)?{

      //通過添加和刪除加載的回調并通知來管理加載的類

      //加載完成時回調。

      //咱都沒數據肯定沒加載完成,這個不管。急著往下看

      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);

      }

      //同上,接著向下看

      EngineJob?engineJob?=

      engineJobFactory.build(

      key,

      isMemoryCacheable,

      useUnlimitedSourceExecutorPool,

      useAnimationPool,

      onlyRetrieveFromCache);

      //負責從緩存數據或原始源解碼資源的類,看著像,咱看看DecodeJob

      //應用轉換和代碼轉換。

      DecodeJob?decodeJob?=

      decodeJobFactory.build(

      ...

      engineJob);

      jobs.put(key,?engineJob);

      engineJob.addCallback(cb,?callbackExecutor);

      engineJob.start(decodeJob);

      if?(VERBOSE_IS_LOGGABLE)?{

      logWithTimeAndKey("Started?new?load",?startTime,?key);

      }

      return?new?LoadStatus(cb,?engineJob);

      }

      9.DecodeJob

      class?DecodeJob

      implements?DataFetcherGenerator.FetcherReadyCallback,

      Runnable,

      Comparable>,

      Poolable?{

      }

      ...

      //構造方法有個DiskCacheProvider看著跟磁盤緩存有關咱進去瞅瞅

      DecodeJob(DiskCacheProvider?diskCacheProvider,?Pools.Pool>?pool)?{

      this.diskCacheProvider?=?diskCacheProvider;

      this.pool?=?pool;

      }

      ...

      10.DiskCacheProvider

      磁盤緩存實現的入口。

      在指定的內存中創建基于{@link com.bumptech.glide.disklrucache.disklrucache}的磁盤緩存。

      磁盤緩存目錄。

      public?class?DiskLruCacheFactory?implements?DiskCache.Factory?{

      private?final?long?diskCacheSize;

      private?final?CacheDirectoryGetter?cacheDirectoryGetter;

      /**?在UI線程外調用接口以獲取緩存文件夾。?*/

      public?interface?CacheDirectoryGetter?{

      File?getCacheDirectory();

      }

      public?DiskLruCacheFactory(final?String?diskCacheFolder,?long?diskCacheSize)?{

      this(

      new?CacheDirectoryGetter()?{

      @Override

      public?File?getCacheDirectory()?{

      return?new?File(diskCacheFolder);

      }

      },

      diskCacheSize);

      }

      public?DiskLruCacheFactory(

      final?String?diskCacheFolder,?final?String?diskCacheName,?long?diskCacheSize)?{

      this(

      new?CacheDirectoryGetter()?{

      @Override

      public?File?getCacheDirectory()?{

      return?new?File(diskCacheFolder,?diskCacheName);

      }

      },

      diskCacheSize);

      }

      /**

      *使用此構造函數時,將調用{@link?CacheDirectoryGetter#getCacheDirectory()}

      *UI線程,允許在不影響性能的情況下進行I/O訪問。

      *在UI線程外調用@param?cacheDirectoryGetter接口以獲取緩存文件夾。

      *@param?diskCacheSize?LRU磁盤緩存所需的最大字節大小。

      */

      //?Public?API.

      @SuppressWarnings("WeakerAccess")

      public?DiskLruCacheFactory(CacheDirectoryGetter?cacheDirectoryGetter,?long?diskCacheSize)?{

      this.diskCacheSize?=?diskCacheSize;

      this.cacheDirectoryGetter?=?cacheDirectoryGetter;

      }

      @Override

      public?DiskCache?build()?{

      File?cacheDir?=?cacheDirectoryGetter.getCacheDirectory();

      if?(cacheDir?==?null)?{

      return?null;

      }

      if?(cacheDir.isDirectory()?||?cacheDir.mkdirs())?{

      return?DiskLruCacheWrapper.create(cacheDir,?diskCacheSize);

      }

      return?null;

      }

      }

      11.DiskCache.Factory

      DiskLruCacheFactory實現的接口是什么,咱看看

      /**?用于向磁盤緩存寫入數據和從磁盤緩存讀取數據的接口?*/

      public?interface?DiskCache?{

      /**?用于創建磁盤緩存的接口?*/

      interface?Factory?{

      /**?250?MB?of?cache.?*/

      int?DEFAULT_DISK_CACHE_SIZE?=?250?*?1024?*?1024;

      String?DEFAULT_DISK_CACHE_DIR?=?"image_manager_disk_cache";

      /**?返回新的磁盤緩存,如果無法創建磁盤緩存,則返回{@code?null}*/

      @Nullable

      DiskCache?build();

      }

      /**?向磁盤緩存中的密鑰實際寫入數據的接口?*/

      interface?Writer?{

      /**

      *將數據寫入文件

      *如果寫入操作應中止,則返回false。

      *@param?file寫入程序應寫入的文件。

      */

      boolean?write(@NonNull?File?file);

      }

      /**

      *獲取給定鍵處的值的緩存。

      */

      @Nullable

      File?get(Key?key);

      /**

      *@param?key要寫入的密鑰。

      *@param?writer一個接口,該接口將在給定密鑰輸出流的情況下寫入數據。

      */

      void?put(Key?key,?Writer?writer);

      /**

      *?從緩存中刪除鍵和值。.

      Android Glide 緩存機制及源碼

      */

      @SuppressWarnings("unused")

      void?delete(Key?key);

      /**?Clear?the?cache.?*/

      void?clear();

      }

      磁盤緩存寫入和讀取的接口有了,那其他相關聯的源碼找到試著理解也是沒問題的,再多找就亂了。

      LRU是什么

      LRU是近期最少使用的算法(緩存淘汰算法),它的核心思想是當緩存滿時,會優先淘汰那些近期最少使用的緩存對象。采用LRU算法的緩存有兩種:LrhCache和DisLruCache,分別用于實現內存緩存和硬盤緩存,其核心思想都是LRU緩存算法。

      LruCache的核心思想很好理解,就是要維護一個緩存對象列表,其中對象列表的排列方式是按照訪問順序實現的,即一直沒訪問的對象,將放在隊尾,即將被淘汰。而最近訪問的對象將放在隊頭,最后被淘汰。

      內存緩存的LRU

      /**?An?LRU?in?memory?cache?for?{@link?com.bumptech.glide.load.engine.Resource}s.?*/

      public?class?LruResourceCache?extends?LruCache>?implements?MemoryCache?{

      private?ResourceRemovedListener?listener;

      /**

      *LruResourceCache的構造函數。

      *@param?size內存緩存可以使用的最大字節大小。

      */

      public?LruResourceCache(long?size)?{

      super(size);

      }

      @Override

      public?void?setResourceRemovedListener(@NonNull?ResourceRemovedListener?listener)?{

      this.listener?=?listener;

      }

      @Override

      protected?void?onItemEvicted(@NonNull?Key?key,?@Nullable?Resource?item)?{

      if?(listener?!=?null?&&?item?!=?null)?{

      listener.onResourceRemoved(item);

      }

      }

      @Override

      protected?int?getSize(@Nullable?Resource?item)?{

      if?(item?==?null)?{

      return?super.getSize(null);

      }?else?{

      return?item.getSize();

      }

      }

      @SuppressLint("InlinedApi")

      @Override

      public?void?trimMemory(int?level)?{

      if?(level?>=?android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND)?{

      //正在輸入緩存的后臺應用程序列表

      //退出我們的整個Bitmap緩存

      clearMemory();

      }?else?if?(level?>=?android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN

      ||?level?==?android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL)?{

      //?The?app's?UI?is?no?longer?visible,?or?app?is?in?the?foreground?but?system?is?running

      //?critically?low?on?memory

      //?Evict?oldest?half?of?our?bitmap?cache

      trimToSize(getMaxSize()?/?2);

      }

      }

      }

      LruCache

      存在一個LinkedHashMap存放數據,并且實現了LRU(最少使用算法)緩存策略。

      Map?cache?=?new?LinkedHashMap<>(100,0.75f,?true):

      其中第二個參數0.75f表示加載因子,即容量達到75%的時候會把內存臨時增加一倍。

      最后這個參數也至關重要,表示訪問元素的排序方式,true表示按照訪問順序排序,false表示按敗插入的順序排序。

      LruCache實現原理

      利用了LinkedHashMap排序方式的特性:由于使用訪問順序排序,進行了get/put操作的元素會放在Map最后面。所以當最后一個元素插入進來時,如果當前的緩存數據大小超過了最大限制,那么會刪除Map中放在前面的元素。

      往期回顧

      RecyclerView 繪制流程及Recycler緩存

      Java的四種引用方式

      Glide 的簡單使用

      Android

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:成功解決當Win10系統進行深度學習的時候發現系統C盤滿了,教你如何正確卸載一些非必要的內容
      下一篇:ECCV 2020 實例分割+全景分割論文大盤點
      相關文章
      亚洲国产精品日韩专区AV| 国产亚洲精午夜久久久久久| 国产亚洲精品免费| 亚洲国产日韩精品| 久久精品国产亚洲AV无码偷窥 | 亚洲日韩精品无码一区二区三区| 高清在线亚洲精品国产二区| 亚洲AV无码AV日韩AV网站| 亚洲AV永久无码天堂影院 | 亚洲熟女综合色一区二区三区| 亚洲成av人片不卡无码| 中文字幕亚洲色图| 亚洲精品日韩专区silk| 亚洲国产视频网站| 亚洲91精品麻豆国产系列在线| 亚洲三级在线播放| 亚洲深深色噜噜狠狠网站| 亚洲精品天堂在线观看| 亚洲国产欧洲综合997久久| 亚洲国产精品无码久久| 色欲色欲天天天www亚洲伊| 成人婷婷网色偷偷亚洲男人的天堂 | 自拍日韩亚洲一区在线| 亚洲国产综合精品中文第一| 亚洲综合色一区二区三区| 亚洲精品无码一区二区| 日韩精品亚洲专区在线影视| 亚洲 无码 在线 专区| 久久国产成人精品国产成人亚洲| 亚洲色欲久久久综合网| 亚洲av福利无码无一区二区| 亚洲老熟女@TubeumTV| 亚洲mv国产精品mv日本mv| 亚洲日韩乱码中文字幕| 国产亚洲日韩在线a不卡| 久久精品亚洲乱码伦伦中文| 久久精品国产亚洲av麻| 亚洲狠狠狠一区二区三区| 亚洲入口无毒网址你懂的| 亚洲丁香婷婷综合久久| 亚洲男人av香蕉爽爽爽爽|