news 2026/4/15 14:41:30

【Android】Glide的缓存机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Android】Glide的缓存机制

Application Options(选项)

Glide允许应用通过AppGlideModule实现完全控制Glide的内存和磁盘应用缓存。Glide对大部分应用提供合理的默认选项,部分应用需要定制。

Memory cache(内存缓存)

  1. 自定义MemoryCache的大小

    在GlideModule中使用applyOptions方法配置MemorySizeCalculator

    @GlideModule(glideName="GlideApp")publicclassMyGlideModuleextendsAppGlideModule{publicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){MemorySizeCalculatorcalculator=newMemorySizeCalculator.Builder(context).setMemoryCacheScreens(2).build();builder.setMemoryCache(newLruResourceCache(calculator.getMemoryCacheSize()));}}
  2. 直接覆盖缓存大小

    @GlideModulepublicclassMyGlideModuleextendsAppGlideModule{@OverridepublicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){intmemoryCacheSizeBytes=1024*1024*10;// 10mbbuilder.setMemoryCache(newLruResourceCache(memoryCacheSizeBytes));}}
  3. 提供自己的MemoryCache实现

    @GlideModulepublicclassMyGlideModuleextendsAppGlideModule{@OverridepublicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){builder.setMemoryCache(newMyMemoryCacheImpl());}}

Disk Cache(磁盘缓存)

Glide 使用DiskLruCacheWrapper作为默认的磁盘缓存。 DiskLruCacheWrapper 是一个使用 LRU 算法的固定大小的磁盘缓存。默认磁盘大小为250 MB,位置是在应用的缓存文件夹中的一个特定目录

  1. 如果显示的媒体是公开的,则应用程序可以将位置更改为外部存储

    @GlideModulepublicclassMyGlideModuleextendsAppGlideModule{@OverridepublicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){builder.setDiskCache(newExternalCacheDiskCacheFactory(context));}}
  2. 无论使用内部或外部磁盘存储,应用程序都可以改变磁盘缓存的大小

    @GlideModulepublicclassMyGlideModuleextendsAppGlideModule{@OverridepublicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){intdiskCacheSizeBytes=1024*1024*100;// 100 MBbuilder.setDiskCache(newInternalCacheDiskCacheFactory(context,diskCacheSizeBytes));}}
  3. 应用程序可以选择DiskCache接口的实现,并提供自己的DiskCache.Factory来创建缓存

    @GlideModulepublicclassMyGlideModuleextendsAppGlideModule{@OverridepublicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){builder.setDiskCache(newDiskCache.Factory(){@OverridepublicDiskCachebuild(){returnnewMyCustomDiskCache();}});}}

BitmapPool(位图池)

BitmapPool 是 Glide 内部用于管理和复用Bitmap对象的内存缓存区。它的主要目的是避免频繁创建和销毁Bitmap对象,从而减少内存抖动和垃圾回收(GC)压力,显著提升应用在加载大量图片时的流畅性和性能。

  1. 可以在它们的 AppGlideModule 中定制 BitmapPool 的尺寸,使用 applyOptions(Context, GlideBuilder) 方法并配置 MemorySizeCalculator

    @GlideModulepublicclassMyGlideModuleextendsAppGlideModule{@OverridepublicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){MemorySizeCalculatorcalculator=newMemorySizeCalculator.Builder(context).setBitmapPoolScreens(3).build();builder.setBitmapPool(newLruBitmapPool(calculator.getBitmapPoolSize()));}}
  2. 直接复写这个池的大小

    @GlideModulepublicclassMyGlideModuleextendsAppGlideModule{@OverridepublicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){intbitmapPoolSizeBytes=1024*1024*30;// 30mbbuilder.setBitmapPool(newLruBitmapPool(bitmapPoolSizeBytes));}}
  3. 提供自己的BitmapPool实现

    @GlideModulepublicclassMyGlideModuleextendsAppGlideModule{@OverridepublicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){builder.setBitmapPool(newMyBitmapPoolImpl());}}

缓存机制

默认情况下,Glide会在开始一个新的图片的请求之前检查以下多级的缓存:

  • 活动资源(Activity Resources):现在是否有另一个View正在展示这张图片
  • 内存缓存(Memory cache):该图片是否最近被加载过并存于内存
  • 资源缓存(Resource):该图片是否之前被解码、转换并写入磁盘缓存
  • 数据来源(Data):构建这个图片的资源是否之前曾被写入过文件缓存

如果四个步骤都未找到图片,则Glide会返回到原始资源以取回数据

三级缓存

  • 内存缓存:优先加载,速度最快
  • 本地缓存:其次加载,速度快
  • 网络缓存:最后加载,速度慢,浪费流量

Glide使用了ActiveResource(活动缓存弱引用)+MemoryCache(内存缓存Lru算法)+DiskCache(磁盘缓存Lru算法)

  • ActivityResources:存储当前界面使用的图片。界面不展示后,该Bitmap被缓存至MemoryCache,并从ActiveResources中删除
  • Memory Cache:存储当前没有使用到的Bitmap,当从MemoryCache中得到后,被存储到ActiveResources中,并从MemoryCache中删除
  • Disk Cache:持久缓存,图片被处理后会缓存到文件中,应用再次被打开时可以加载缓存直接使用

注意:ActiveResource+MemoryCache属于内存缓存,二者不共存,应用杀死后不存在

配置缓存

磁盘缓存策略

DiskCacheStrategy可被diskCacheStrategy方法应用到每一个单独的请求。目前支持的策略允许阻止加载过程使用或写入磁盘缓存,选择性的仅缓存无修改的原生数据或仅缓存变换过的缩略图或二者都有。

指定DiskCacheStrategy:

Glide.with(this).load(uri).diskCacheStrategy(DiskCacheStrategy.ALL).into(imageView);

diskCacheStrategy方法可传入的参数有五种:

  • DiskCacheStrategy.ALL:既缓存原始图片,也缓存转换后的图片
  • DiskCacheStrategy.NONE:不缓存任何内容
  • DiskCacheStrategy.DATA:只缓存原始图片
  • DiskCacheStrategy.RESULT:只缓存转换后的图片

仅从缓存加载图片

实现图片不在缓存中则加载直接失败,可以在单个请求的基础上使用onlyRetrieveFromCache方法

Glide.with(this).load(uri).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC).onlyRetrieveFromCache(true);.into(imageView);

跳过缓存

确保一个特定的请求跳过磁盘或内存缓存。

  • 仅跳过内存缓存,使用skipMemoryCache方法

    Glide.with(fragment).load(url).skipMemoryCache(true).into(imageView);
  • 仅跳过磁盘缓存,使用DiskCacheStrategy.NONE

    Glide.with(fragment).load(url).diskCacheStrategy(DiskCacheStrategy.NONE).into(imageView);
  • 以上两个选项可以同时使用

清理磁盘缓存

要尝试清理所有磁盘缓存条目,可使用clearDiskCache

newThread(newRunnable(){@Overridepublicvoidrun(){try{// 必须在子线程上调用此方法。Glide.get(AppGlobalUtils.getApplication()).clearDiskCache();}catch(Exceptione){e.printStackTrace();}}}).start();

Glide源码分析

加载流程

Engine类负责启动加载并管理活动资源和缓存资源,其中的load方法提供路径加载图片

public<R>LoadStatusload(GlideContextglideContext,Objectmodel,Keysignature,intwidth,intheight,Class<?>resourceClass,Class<R>transcodeClass,Prioritypriority,DiskCacheStrategydiskCacheStrategy,Map<Class<?>,Transformation<?>>transformations,booleanisTransformationRequired,booleanisScaleOnlyOrNoTransform,Optionsoptions,booleanisMemoryCacheable,booleanuseUnlimitedSourceExecutorPool,booleanuseAnimationPool,booleanonlyRetrieveFromCache,ResourceCallbackcb,ExecutorcallbackExecutor){longstartTime=VERBOSE_IS_LOGGABLE?LogTime.getLogTime():0;//EngineKey:用于多路传输加载的仅内存缓存密钥EngineKeykey=keyFactory.buildKey(model,signature,width,height,transformations,resourceClass,transcodeClass,options);EngineResource<?>memoryResource;synchronized(this){//重点:调用了loadFromMemory方法memoryResource=loadFromMemory(key,isMemoryCacheable,startTime);if(memoryResource==null){returnwaitForExistingOrStartNewJob(glideContext,model,signature,width,height,resourceClass,transcodeClass,priority,diskCacheStrategy,transformations,isTransformationRequired,isScaleOnlyOrNoTransform,options,isMemoryCacheable,useUnlimitedSourceExecutorPool,useAnimationPool,onlyRetrieveFromCache,cb,callbackExecutor,key,startTime);}}// 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);returnnull;}

下面来看loadFromMemory()

@NullableprivateEngineResource<?>loadFromMemory(EngineKeykey,booleanisMemoryCacheable,longstartTime){if(!isMemoryCacheable){returnnull;}//根据key去活动缓存中找EngineResource<?>active=loadFromActiveResources(key);if(active!=null){if(VERBOSE_IS_LOGGABLE){logWithTimeAndKey("Loaded resource from active resources",startTime,key);}//找到了直接返回数据returnactive;}//找不到接着去内存缓存中找EngineResource<?>cached=loadFromCache(key);if(cached!=null){if(VERBOSE_IS_LOGGABLE){logWithTimeAndKey("Loaded resource from cache",startTime,key);}returncached;}returnnull;}

之后我们分别来看活动缓存和内存缓存

活动缓存

privateEngineResource<?>loadFromActiveResources(Keykey){// activeResources:活动缓存EngineResource<?>active=activeResources.get(key);if(active!=null){active.acquire();}returnactive;}@NullablesynchronizedEngineResource<?>get(Keykey){//弱引用ResourceWeakReferenceactiveRef=activeEngineResources.get(key);if(activeRef==null){returnnull;}EngineResource<?>active=activeRef.get();if(active==null){cleanupActiveReference(activeRef);}returnactive;}@SyntheticvoidcleanupActiveReference(@NonNullResourceWeakReferenceref){synchronized(this){//从活动缓存中删除资源activeEngineResources.remove(ref.key);if(!ref.isCacheable||ref.resource==null){return;}}EngineResource<?>newResource=newEngineResource<>(ref.resource,/*isMemoryCacheable=*/true,/*isRecyclable=*/false,ref.key,listener);listener.onResourceReleased(ref.key,newResource);}@OverridepublicvoidonResourceReleased(KeycacheKey,EngineResource<?>resource){activeResources.deactivate(cacheKey);if(resource.isMemoryCacheable()){//将活动缓存数据写入内存缓存cache.put(cacheKey,resource);}else{resourceRecycler.recycle(resource,/*forceNextFrame=*/false);}}

内存缓存

privateEngineResource<?>loadFromCache(Keykey){//从内存缓存中删除数据EngineResource<?>cached=getEngineResourceFromCache(key);if(cached!=null){cached.acquire();//将数据保存至活动缓存activeResources.activate(key,cached);}returncached;}privateEngineResource<?>getEngineResourceFromCache(Keykey){//cache:MemoryCache类型Resource<?>cached=cache.remove(key);finalEngineResource<?>result;if(cached==null){result=null;}elseif(cachedinstanceofEngineResource){// Save an object allocation if we've cached an EngineResource (the typical case).result=(EngineResource<?>)cached;}else{result=newEngineResource<>(cached,/*isMemoryCacheable=*/true,/*isRecyclable=*/true,key,/*listener=*/this);}returnresult;}

如果两种缓存都未找到,说明图片未保存,就会调用waitForExistingOrStartNewJob方法

private<R>LoadStatuswaitForExistingOrStartNewJob(...){//通过添加和删除加载的回调并通知来管理加载的类,在加载完成时回调EngineJob<?>current=jobs.get(key,onlyRetrieveFromCache);if(current!=null){current.addCallback(cb,callbackExecutor);if(VERBOSE_IS_LOGGABLE){logWithTimeAndKey("Added to existing load",startTime,key);}returnnewLoadStatus(cb,current);}EngineJob<R>engineJob=engineJobFactory.build(key,isMemoryCacheable,useUnlimitedSourceExecutorPool,useAnimationPool,onlyRetrieveFromCache);//负责从缓存数据或原始源解码资源的类DecodeJob<R>decodeJob=decodeJobFactory.build(glideContext,model,key,signature,width,height,resourceClass,transcodeClass,priority,diskCacheStrategy,transformations,isTransformationRequired,isScaleOnlyOrNoTransform,onlyRetrieveFromCache,options,engineJob);jobs.put(key,engineJob);engineJob.addCallback(cb,callbackExecutor);engineJob.start(decodeJob);if(VERBOSE_IS_LOGGABLE){logWithTimeAndKey("Started new load",startTime,key);}returnnewLoadStatus(cb,engineJob);}
classDecodeJob<R>implementsDataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable{}...//DiskCacheProvider跟磁盘缓存有关DecodeJob(DiskCacheProviderdiskCacheProvider,Pools.Pool<DecodeJob<?>>pool){this.diskCacheProvider=diskCacheProvider;this.pool=pool;}...

LRU算法

近期最少使用算法,核心思想就是当缓存满时,淘汰最少使用的缓存对象

实现内存缓存的LruCache和硬盘缓存的DisLruCache的核心思想都是LRU缓存算法。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/11 0:39:31

计算机毕业设计springboot餐厅预定系统 基于SpringBoot的智慧餐饮订座平台 SpringBoot驱动的线上餐厅席位预约管理系统

计算机毕业设计springboot餐厅预定系统693j34gy &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。移动互联网把“到店即食”变成了“到店前决策”&#xff0c;餐厅空座率与顾客等位…

作者头像 李华
网站建设 2026/4/11 17:20:44

被老板追着问的省钱秘籍,竟然是它!

传统资产管理的 “痛点” 大揭秘在企业运营的庞大体系中&#xff0c;资产管理堪称关键一环。然而&#xff0c;不少企业还在沿用传统的资产管理方式&#xff0c;殊不知&#xff0c;这些方式正悄然成为企业降本增效路上的 “绊脚石”。人工盘点&#xff1a;耗时耗力的 “持久战”…

作者头像 李华
网站建设 2026/4/15 7:10:12

传统修复 vs AI修复:DirectX问题处理效率提升300%

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个DirectX修复效率对比工具&#xff1a;1.传统修复流程模拟(手动下载、安装等) 2.AI修复流程实现 3.自动记录各步骤耗时 4.生成对比图表 5.支持导出测试报告。要求使用Python…

作者头像 李华
网站建设 2026/4/11 17:29:36

用HBuilderX快速原型设计:1小时打造产品Demo

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个电商APP的快速原型&#xff0c;包含商品列表、商品详情和购物车功能。使用HBuilderX和uni-app框架&#xff0c;要求界面美观&#xff0c;有基本的交互效果&#xff08;如点…

作者头像 李华
网站建设 2026/4/15 5:51:21

解锁Git高阶技能:Rebase、Stash与子模块的奇妙之旅

引言在当今软件开发的世界里&#xff0c;版本控制系统是开发者不可或缺的工具&#xff0c;而 Git 无疑是其中的佼佼者。它以强大的功能、高效的分布式特性以及丰富的命令集&#xff0c;成为了全球开发者首选的版本管理工具。无论是个人开发者在小型项目中的代码管理&#xff0c…

作者头像 李华
网站建设 2026/4/15 10:41:11

告别IDEA卡顿!全方位性能调优秘籍大放送

一、引言在软件开发的世界里&#xff0c;IntelliJ IDEA 凭借其强大的功能和丰富的插件生态&#xff0c;成为了众多开发者的首选集成开发环境。然而&#xff0c;随着项目规模的不断扩大以及对代码质量要求的日益提高&#xff0c;不少开发者都遭遇过 IDEA 卡顿的困扰。想象一下&a…

作者头像 李华