当前位置: 首页 > news >正文

Glide 缓存机制研究及同命名图片的替换回显

背景:

相册-图片后处理场景,需要先展示一张原图,同时后台对图片进行算法优化,完成优化之后无缝替换原图展示,同时保证后续都展示算法优化后的图片

图片加载采用 Glide 库实现

画重点:

  1. 相册场景:意味着存在图片缓存的复用

  2. 无缝替换:切换过程要很丝滑,不能有闪烁等现象

  3. 后续展示优化后的图片:说明后续缓存要复用算法优化后的,不能用原图

  4. 因为是对图片进行优化,所以原图和算法优化后的图片文件名一样

最开始的想法:

步骤:

  1. 先加载原图展示

  2. 等图片算法优化,重新调用 glide 加载一遍

结果:

  1. 重新加载之后,还是原图效果

Glide 缓存机制

基于上述原因,专门研究了下 Glide 的缓存机制,重点是缓存的读取这一块

四级缓存机制

Glide 采用四级缓存机制,主要分成两个模块,内存缓存,硬盘缓存

  • 内存缓存:防止应用重复将图片数据读到内存中
  • 硬盘缓存:防止应用重复从网络或者其他地方重复下载和读取数据
  • 缓存Key:既然有缓存,那肯定需要对应的缓存Key,通过按照一定规则生成缓存key之后,根据key从缓存集中取出对应缓存复用

这里先简单讲下四级缓存机制,然后再跟着源码过一遍

内存缓存-lru缓存:

Glide 在内存中维持的一个缓存 map,通过 lru 算法实现内存管理

private final LinkedHashMap<T, Y> cache = new LinkedHashMap<T, Y>(100, 0.75f, true);
​
T:缓存Key
Y:缓存资源

内存缓存-弱引用缓存:

Glide 在内存中维持的一个弱引用缓存 map,由于是弱引用,这就意味着内存不足的时候先销毁这个

private final Map<Key, WeakReference<EngineResource<?>>> activeResources;

硬盘缓存-转换过缓存:

Glide 在硬盘上的缓存,该缓存是经过各种变换后的缓存,不是原始图片

硬盘缓存-原始图片缓存:

Glide 在硬盘上的缓存,该缓存是原始图片缓存

Glide 缓存加载流程:

public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {Util.assertMainThread();long startTime = LogTime.getLogTime();
​final String id = fetcher.getId();// 1. 生成 缓存key,可以看出 key 的生成用到很多参数,比如 id(图片路径)、signature、width、height,还有 transformationEngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),transcoder, loadProvider.getSourceEncoder());
​// 2. 先从内存缓存-lru缓存中拿缓存,如果拿到缓存调用 onResourceReadyEngineResource<?> cached = loadFromCache(key, isMemoryCacheable);if (cached != null) {cb.onResourceReady(cached);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Loaded resource from cache", startTime, key);}return null;}// 3. 内存缓存-lru中没有缓存,则从弱引用缓存中拿,如果拿到调用 onResourceReadyEngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);if (active != null) {cb.onResourceReady(active);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Loaded resource from active resources", startTime, key);}return null;}// 4. 内存缓存-弱引用中也没有缓存,从硬盘缓存中拿或加载文件地址,不管是拿硬盘缓存还是加载文件,涉及到IO操作,需要子线程处理EngineJob current = jobs.get(key);if (current != null) {current.addCallback(cb);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Added to existing load", startTime, key);}return new LoadStatus(cb, current);}
​EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,transcoder, diskCacheProvider, diskCacheStrategy, priority);EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);jobs.put(key, engineJob);engineJob.addCallback(cb);engineJob.start(runnable);
​if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Started new load", startTime, key);}return new LoadStatus(cb, engineJob);
}

内存缓存-lru缓存中拿缓存:

private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {if (!isMemoryCacheable) {return null;}// 如果从 lru 中能拿到缓存,将该缓存从 lru 缓存中移除,并存入 弱引用缓存EngineResource<?> cached = getEngineResourceFromCache(key);if (cached != null) {cached.acquire();activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));}return cached;
}

从硬盘缓存中拿或加载文件地址:

private Resource<?> decode() throws Exception {// 这里有个策略判断,只有支持硬盘缓存策略,才能从硬盘中拿缓存if (isDecodingFromCache()) {// 从硬盘缓存中拿return decodeFromCache();} else {// 加载文件return decodeFromSource();}
}​
// 从硬盘缓存中拿
private Resource<?> decodeFromCache() throws Exception {Resource<?> result = null;try {// 5. 从硬盘缓存-转换过缓存中拿result = decodeJob.decodeResultFromCache();} catch (Exception e) {if (Log.isLoggable(TAG, Log.DEBUG)) {Log.d(TAG, "Exception decoding result from cache: " + e);}}
​if (result == null) {// 6. 硬盘缓存-转换过缓存中拿不到,从原始图片缓存中拿result = decodeJob.decodeSourceFromCache();}return result;
}
​
// 从硬盘缓存-转换过缓存中拿
public Resource<Z> decodeResultFromCache() throws Exception {if (!diskCacheStrategy.cacheResult()) {return null;}
​long startTime = LogTime.getLogTime();Resource<T> transformed = loadFromCache(resultKey);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Decoded transformed from cache", startTime);}startTime = LogTime.getLogTime();Resource<Z> result = transcode(transformed);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Transcoded transformed from cache", startTime);}return result;
}
​
// 从硬盘缓存-原始图片缓存中拿
public Resource<Z> decodeSourceFromCache() throws Exception {if (!diskCacheStrategy.cacheSource()) {return null;}
​long startTime = LogTime.getLogTime();// 这里值得注意的是,这里用的key和其他三个缓存中用到的不一样,这里用的是 resultKey.getOriginalKey() 就是原始图片地址,不过也能理解,加载原始图片缓存,也不需要额外的key信息,只需要文件地址就行了Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Decoded source from cache", startTime);}return transformEncodeAndTranscode(decoded);
}

理完Glide的加载机制之后,就很好理解为什么重新加载之后还是原图,所以进行了优化

优化方案二

加载原图时:

glideOptions = new RequestOptions().signature(new ObjectKey(signatureKey));

加载优化后图片时:

glideOptions = new RequestOptions();

加载原图时多调用了 signature 方法,这样加载优化后图片时,由于和加载原图时的缓存key不一样,会重新进行加载

优化方案二解决了优化图片不加载问题,但是由于重新加载图片,会导致切换过程出现很明显的闪烁,这里可以通过 placeholder 方法优化

优化方案三

// 1. 获取当前 imageView 的 drawable
Drawable drawable = imageView.getDrawable();
Bitmap placeholderBitmap = null;
// 2. drawable 转成 bitmap
if(drawable instanceof BitmapDrawable){placeholderBitmap = ((BitmapDrawable) drawable).getBitmap();
}
// 3. 由于 imageView 的 drawable 可能会被回收,所以需要将 bitmap 重新 clone 一份
Bitmap copiedBitmap = null;
if(placeholderBitmap != null){copiedBitmap = placeholderBitmap.copy(placeholderBitmap.getConfig(), true);
}
// 4. 将 bitmap 作为 占位符传入
if(copiedBitmap != null && !copiedBitmap.isRecycled()){glideOptions = new RequestOptions().frame(0).placeholder(new BitmapDrawable(context.getResources(), copiedBitmap)).override(size.getWidth(), size.getHeight());
}else {glideOptions = new RequestOptions().frame(0).override(size.getWidth(), size.getHeight());
}

至此,效果实现!

相关文章:

Glide 缓存机制研究及同命名图片的替换回显

背景&#xff1a; 相册-图片后处理场景&#xff0c;需要先展示一张原图&#xff0c;同时后台对图片进行算法优化&#xff0c;完成优化之后无缝替换原图展示&#xff0c;同时保证后续都展示算法优化后的图片 图片加载采用 Glide 库实现 画重点&#xff1a; 相册场景&#xff…...

一键简易桌签(带背景)-Word插件-大珩助手

问题整理&#xff1a; 如何Word中设计简易桌签&#xff1f;如何设置带背景图的桌签&#xff1f; Word大珩助手是一款功能丰富的Office Word插件&#xff0c;旨在提高用户在处理文档时的效率。它具有多种实用的功能&#xff0c;能够帮助用户轻松修改、优化和管理Word文件&…...

如何解决centos停止维护后更换Debian11平替?

再有9天时间centos7.x就将停止维护,最近很多人已经再更换系统了!有推荐alibabacloud,opencloud,debian,ubuntu,centos-stream,tencentos,anolis,rockey,almal,oracle,arch,Fedora,opensuse 很多人不知道选什么,这里就主要介绍一下 如果你是使用宝塔面板 并且想要无缝衔…...

服务器雪崩的应对策略之----熔断机制

熔断机制&#xff08;Circuit Breaker&#xff09;是一种保护系统稳定性的重要手段。它的主要目的是防止系统在依赖的服务出现问题时&#xff0c;继续发送请求&#xff0c;从而保护系统免受进一步的影响。熔断机制通过监控请求的成功和失败率&#xff0c;在检测到故障率超过预设…...

RestClient操作索引库和文档

导入依赖&#xff1a; <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.12.1</version></dependency>导入依赖成功之后就可以开始我们的测…...

【设计模式深度剖析】【11】【行为型】【解释器模式】| 以算术表达式求值为例加深理解

&#x1f448;️上一篇:状态模式 设计模式-专栏&#x1f448;️ 文章目录 解释器模式定义英文原话直译 解释器模式中的角色1. 抽象表达式&#xff08;AbstractExpression&#xff09;2. 终端表达式&#xff08;TerminalExpression&#xff09;3. 非终端表达式&#xff08;Non…...

MySQL8,Navicat能登陆成功,密码却忘记了

执行成功的图&#xff1a; 以下为步骤&#xff1a;本文一共8个简单步骤。 环境&#xff1a;mysql8、window10、navicat11 1、打开本地电脑window10的命令窗&#xff08;俗称黑窗口&#xff09;&#xff0c;windowR 2、输入regegit&#xff0c;回车&#xff0c;打开注册表 3、…...

游戏中的寻路算法研究

1)Unity NavMesh研究 思考&#xff1a;毫无疑问&#xff0c;unity中自带的navmesh寻路是比较健全的&#xff0c;无奈在服务器中无法使用&#xff0c;这样子我们没办法做怪的导航&#xff0c;但是可以先体验下都有哪些api&#xff0c;并且可以可视化的看效果。 1)打开导航网格…...

【AWS SMB】关于AWS 中小型企业 (SMB) 能力介绍及注意事项

文章目录 前言一、什么是 SMB&#xff1f;&#x1f4e2;二、如何识别中小企业的需求三、中小企业营销活动的类型四、AWS 合作伙伴可获得的其他 AWS 机会4.1 AWS IQ4.2 APN 客户参与 (ACE) 计划 前言 AWS 中小型企业 &#xff08;SMB&#xff09; 能力合作伙伴专注于帮助中小型…...

中年之恋:重返青春的旅程

第一章&#xff1a;重逢 在一个普通的周末&#xff0c;李明参加了一次由老同学组织的聚会&#xff0c;尽管他对此并不抱有太大的兴趣&#xff0c;但出于礼貌还是选择了出席。聚会在一家风格复古的咖啡馆里举行&#xff0c;暖黄色的灯光与木质的装饰让人不自觉地放缓了脚步。在…...

人工智能中的监督学习和无监督学习

欢迎来到 Papicatch的博客 目录 &#x1f349;引言 &#x1f349;监督学习 &#x1f348;基本思想 &#x1f348;具体过程 &#x1f34d;数据收集 &#x1f34d;数据预处理 &#x1f34d;模型选择 &#x1f34d;模型训练 &#x1f34d;模型评估 &#x1f34d;模型部署…...

深度学习500问——Chapter12:网络搭建及训练(1)

文章目录 12.1 TensorFlow 12.1.1 TensorFlow 是什么 12.1.2 TensorFlow的设计理念是什么 12.1.3 TensorFlow特点有哪些 12.1.4 TensorFlow的系统架构是怎样的 12.1.5 TensorFlow编程模型是怎样的 12.1.6 如何基于TensorFlow搭建VGG16 12.1 TensorFlow 12.1.1 TensorFlow 是什…...

HuggingFace CLI 命令全面指南

文章目录 安装与认证1.1 安装 HuggingFace Hub 库使用 pip 安装使用 conda 安装验证安装 1.2 认证与登录生成访问令牌使用访问令牌登录环境变量认证验证认证 下载文件2.1 下载单个文件安装 huggingface_hub 库认证与登录下载单个文件 2.2 下载特定版本的文件下载特定版本的文件…...

FreeRTOS源码分析

目录 1、FreeRTOS目录结构 2、核心文件 3、移植时涉及的文件 4、头文件相关 4.1 头文件目录 4.2 头文件 5、内存管理 6、入口函数 7、数据类型和编程规范 7.1 数据类型 7.2 变量名 7.3 函数名 7.4 宏的名 1、FreeRTOS目录结构 使用 STM32CubeMX 创建的 FreeRTOS 工…...

python实战:将视频内容上传到社交媒体平台

在Python中&#xff0c;上传视频到不同的平台可能需要使用不同的API和库。以下是一些常见的平台以及如何使用Python进行上传的示例&#xff1a; YouTube: 使用Google提供的YouTube Data API。 首先&#xff0c;你需要从Google Cloud控制台获取API密钥&#xff0c;并安装google-…...

【深度学习】sdwebui A1111 加速方案对比,xformers vs Flash Attention 2

文章目录 资料支撑资料结论sdwebui A1111 速度对比测试sdxlxformers 用contorlnet sdxlsdpa&#xff08;--opt-sdp-no-mem-attention&#xff09; 用contorlnet sdxlsdpa(--opt-sdp-attention) 用contorlnet sdxl不用xformers或者sdpa ,用contorlnet sdxl不用xformers或者sdpa …...

5分钟了解单元测试

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、什么是单元测试&#xff1f; 单元测试是指&#xff0c;对软件中的最小可测试单元在与程序其…...

VSCode之C/C++插件之宏定义导致颜色变暗

这是因为该宏没有定义或者定义在makefile文件中导致无法被插件识别到&#xff0c;导致误判了 索性将该机制去了&#xff0c;显示也会好看些&#xff0c;如下将C_Cpp下的Dim Inactive Regions勾去了 显示效果会好很多。...

自然语言处理概述

目录 1.概述 2.背景 3.作用 4.优缺点 4.1.优点 4.2.缺点 5.应用场景 5.1.十个应用场景 5.2.文本分类 5.2.1.一般流程 5.2.2.示例 6.使用示例 7.总结 1.概述 自然语言处理&#xff08;NLP&#xff09;是计算机科学、人工智能和语言学的交叉领域&#xff0c;旨在实…...

用Rust和Pingora轻松构建超越Nginx的高效负载均衡器

目录 什么是Pingora&#xff1f;实现过程 初始化项目编写负载均衡器代码代码解析部署 总结 1. 什么是Pingora&#xff1f; Pingora 是一个高性能的 Rust 库&#xff0c;用于构建可负载均衡器的代理服务器&#xff0c;它的诞生是为了弥补 Nginx 存在的缺陷。 Pingora 提供了…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...