面试总结之 Glide自定义的三级缓存策略

一、为什么需要三级缓存?

在移动应用开发中,图片加载性能直接影响用户体验。根据 Google 统计,图片加载延迟超过 1 秒会导致 32% 的用户流失。传统图片加载方案存在以下痛点:

  • 内存占用高:未压缩的大图直接占用大量内存
  • 重复下载:相同图片多次从网络获取
  • 弱网体验差:离线场景无法加载图片

Glide 通过三级缓存策略,将图片加载速度提升 50%,内存占用降低 45%

内存缓存(Memory Cache)

import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.engine.cache.LruResourceCache;
import com.bumptech.glide.module.AppGlideModule;public class CustomGlideModule extends AppGlideModule {@Overridepublic void applyOptions(Context context, GlideBuilder builder) {// 获取设备的最大内存int maxMemory = (int) Runtime.getRuntime().maxMemory();// 计算内存缓存的大小,这里设置为最大内存的15%int memoryCacheSize = maxMemory / 1024 / 1024 * 15;// 创建LruResourceCache对象builder.setMemoryCache(new LruResourceCache(memoryCacheSize));}
}

解释:我们自定义了一个 AppGlideModule 类 CustomGlideModule。在 applyOptions 方法里,先获取设备的最大内存,接着计算出内存缓存的大小,这里设定为最大内存的 15%,最后使用 LruResourceCache 来设置内存缓存。  

  • LruCache 实现:最大缓存容量为应用内存的 15%
  • 缓存键生成策略:包含 URL + 宽高 + 格式的复合键
  • 内存泄漏防护:通过 Glide 生命周期绑定

磁盘缓存(Disk Cache)

import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.engine.cache.DiskLruCacheFactory;
import com.bumptech.glide.module.AppGlideModule;public class CustomDiskCacheGlideModule extends AppGlideModule {@Overridepublic void applyOptions(Context context, GlideBuilder builder) {// 设置磁盘缓存的路径String diskCachePath = context.getCacheDir().getPath() + "/glide_cache";// 设置磁盘缓存的大小为100MBint diskCacheSize = 1024 * 1024 * 100;// 创建DiskLruCacheFactory对象builder.setDiskCache(new DiskLruCacheFactory(diskCachePath, diskCacheSize));}
}

解释:我们同样自定义了一个 AppGlideModule 类 CustomDiskCacheGlideModule。在 applyOptions 方法里,先设置磁盘缓存的路径,再将磁盘缓存的大小设置为 100MB,最后使用 DiskLruCacheFactory 来设置磁盘缓存。

  • 磁盘缓存目录:应用专属缓存目录
  • 缓存淘汰算法:LRU 策略
  • 缓存数据格式:使用 Glide 的默认序列化方式

网络缓存(Network Cache)

import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.AppGlideModule;
import okhttp3.Cache;
import okhttp3.OkHttpClient;import java.io.InputStream;public class CustomNetworkCacheGlideModule extends AppGlideModule {@Overridepublic void registerComponents(Context context, Glide glide, Registry registry) {// 设置网络缓存的路径Cache cache = new Cache(context.getCacheDir(), 1024 * 1024 * 50);// 创建OkHttpClient对象并设置缓存OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();// 注册OkHttpUrlLoader,让Glide使用OkHttp进行网络请求registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));}
}

   解释:自定义了 CustomNetworkCacheGlideModule 类,在 registerComponents 方法中,先设置网络缓存的路径和大小(这里是 50MB),然后创建 OkHttpClient 并设置缓存,最后注册 OkHttpUrlLoader,使 Glide 采用 OkHttp 进行网络请求。

  • 结合 OkHttp 实现 HTTP 缓存
  • 缓存控制策略:根据响应头的 Cache-Control 字段
  • 网络请求重试机制:3 次重试 + 指数退避

使用 Glide 加载图片

import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageView imageView = findViewById(R.id.imageView);String imageUrl = "https://example.com/image.jpg";Glide.with(this).load(imageUrl)// 设置磁盘缓存策略为缓存所有类型的图片.diskCacheStrategy(DiskCacheStrategy.ALL).into(imageView);}
}

在 MainActivity 中,我们使用 Glide 加载图片。通过 diskCacheStrategy(DiskCacheStrategy.ALL) 方法设置磁盘缓存策略,这里表示缓存所有类型的图片。 

 

简单概述:

  1. 内存缓存(手机内存)

    • 存最近看过的图,像手机相册最近项目
    • 优化:只存 15% 内存空间,自动清理不常用的
  2. 磁盘缓存(手机硬盘)

    • 存常用图的备份,像电脑硬盘
    • 优化:存 100MB,优先存高质量图
  3. 网络缓存(路由器缓存)

    • 存已经下载过的图,像小区快递柜
    • 优化:存 50MB,避免重复下载

具体概述

每一层的具体实现

1. 内存缓存(Memory Cache)
  • 作用:快速显示刚看过的图片(如滑动列表时来回切换的图片)
  • 实现
    • 使用 LruCache 算法(最近最少使用),自动清理长时间不用的图片
    • 限制:只占手机可用内存的 15%(例如手机有 8GB 内存,内存缓存约 1.2GB)
  • 优化
    • 图片按屏幕尺寸压缩(比如原图 2000px,手机屏幕 1000px,只存 1000px 版本)
    • 自动清理超出内存限制的图片
2. 磁盘缓存(Disk Cache)
  • 作用:存常用但暂时不在内存中的图片(如用户经常访问的商品详情页图片)
  • 实现
    • 使用 DiskLruCache 存储在手机硬盘
    • 限制:总容量 100MB,优先存高质量图片
  • 优化
    • 按 URL 哈希值命名文件,避免重复存储相同图片
    • 自动清理超过 7 天未使用的图片
3. 网络缓存(Network Cache)
  • 作用:避免重复从服务器下载相同图片(需结合 HTTP 缓存头)
  • 实现
    • 通过 OkHttp 的缓存机制,将图片存在路由器或基站缓存中
    • 限制:总容量 50MB,优先存高频访问的图片
  • 优化
    • 根据 HTTP 的Cache-Control头设置缓存时间(如设置为 1 天)
    • 图片 URL 带版本号(如image_v2.jpg),版本更新时强制重新下载

常见问题解决方案

1. 缓存穿透

Glide.with(context).load(url).error(R.drawable.ic_error) // 设置错误占位图.placeholder(R.drawable.ic_loading) // 设置加载占位图.fallback(R.drawable.ic_fallback) // 设置空值占位图.into(imageView);

2. 缓存雪崩 

int cacheDuration = TimeUnit.HOURS.toMillis(24) + new Random().nextInt(3600000);

3. OOM 预防

// 使用RGB_565格式减少内存占用
Glide.with(context).load(url).format(DecodeFormat.PREFER_RGB_565).into(imageView);

关键指标的获取途径

  1. 冷启动加载时间
    • 工具运用:借助 Android Profiler 的 Timeline 功能来精准测量。
    • 操作办法:在应用启动时,启动 Profiler 并记录图片加载所耗费的时长。
    • 代码示例
long startTime = System.currentTimeMillis();
Glide.with(this).load(url).into(imageView);
long duration = System.currentTimeMillis() - startTime;
Log.d("GlideTest", "加载耗时: " + duration + "ms");
  1. 内存峰值占用情况
    • 工具选择:使用 Android Profiler 的 Memory Monitor 进行监测。
    • 操作步骤:在滑动列表时,留意 Heap Size 的变化趋势。
    • 优化要点:对比开启缓存前后,Bitmap 内存占用的差异。
  2. 缓存命中率计算
    • 工具利用:通过 Glide 的日志输出(设置Glide.get(context).setLogLevel(Log.DEBUG))。
    • 数据提取:从日志中筛选出FetchedDecoded相关的条目。
    • 计算公式:缓存命中率 = (内存命中数 + 磁盘命中数)÷ 总请求数 × 100%。
  3. FPS 帧率监控
    • 工具使用:采用 Android Profiler 的 FrameMetrics 功能。
    • 操作方式:在滑动列表的过程中,记录丢帧的数量。
    • 优化目标:确保平均帧率稳定在 55fps 以上。

扩展追问:

如何解决内存缓存导致的 OOM 问题?

动态尺寸压缩:

Glide.with(context).load(url).override(imageView.width, imageView.height).into(imageView)

格式优化:优先使用 WebP 格式(体积减少 30%)

生命周期绑定:通过Glide.with(this)自动释放资源

内存监控:结合 Android Profiler 设置内存阈值报警

总结:通过合理配置和优化,可显著提升应用性能,降低用户流失率。建议开发者在新项目中优先采用 Glide 的三级缓存方案,并根据实际需求进行定制化调整。

感谢观看!!! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/38315.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用Python实现交互式数据可视化:从基础图表到动态仪表板

用Python实现交互式数据可视化:从基础图表到动态仪表板 一、项目背景 本文将通过一个完整的Python项目,展示如何使用Plotly和ipywidgets构建从基础统计到动态交互的全栈数据可视化方案。 二、核心功能模块 1. 数据生成与预处理 np.random.seed(100)…

Linux进程信号

1.信号的认识 生活中例如闹钟,红绿灯,电话铃声等都属于信号,所白了信号就是中断我们正在做的事情,属于进行事件异步通知机制。 在Linux中信号是发给进程的,信号的产生相较于进程是异步的。 信号的相关知识点&#xff…

Java使用FFmpegFrameGrabber进行视频拆帧,结合Thumbnails压缩图片保存到文件夹

引入依赖 <dependency><groupId>net.coobird</groupId><artifactId>thumbnailator</artifactId><version>0.4.17</version></dependency><dependency><groupId>org.bytedeco</groupId><artifactId>ja…

c++项目-KV存储-模仿redis实现kv键值对存储的基本功能。

KV存储引擎的技术解析&#xff1a;数组、哈希与红黑树实现及其在网络I/O中的应用。 内容概要&#xff1a;本文档深入介绍了基于数组、哈希表和红黑树的键值存储引擎的设计与实现。文档首先阐述了系统的总体架构与类图关系&#xff0c;之后分别对底层存储结构进行了详细解释&am…

vue3:十一、主页面布局(优化页面跳转方式)

:router"true" 一、参考文章 vue3:十一、主页面布局(实现基本左侧菜单右侧内容效果)-CSDN博客 参考上述文章可知&#xff0c;页面跳转是通过在js中定义的菜单中携带的path&#xff0c;然后通过菜单的点击事件完成的跳转&#xff0c;现在可以进行优化&#xff0c;直…

深入解析 Java Stream API:筛选子节点的优雅实现!!!

&#x1f680; 深入解析 Java Stream API&#xff1a;筛选子节点的优雅实现 &#x1f527; 大家好&#xff01;&#x1f44b; 今天我们来聊聊 Java 8 中一个非常常见的操作&#xff1a;使用 Stream API 从 Map 中筛选出特定条件的元素。&#x1f389; 具体来说&#xff0c;我们…

统计学重要概念:自由度

在统计学中&#xff0c;自由度&#xff08;degrees of freedom&#xff0c;简称df&#xff09;是一个重要的概念&#xff0c;它表示在计算某个统计量时可以自由变化的值的数量。对于一个样本量为n的样本&#xff0c;自由度通常为n-1&#xff0c;这是因为我们需要用样本数据来估…

数据结构-排序

文章目录 1. 排序的概念2. 常见排序算法的实现2.1 插入排序1&#xff09;插入排序一&#xff09;基本思想二&#xff09;特性及时间复杂度三&#xff09;代码实现 2&#xff09;希尔排序&#xff08;缩小增量排序&#xff09;一&#xff09;基本思想二&#xff09;特性及时间复…

压缩壳学习

壳是什么 壳就是软件的一个保护套&#xff0c;防止软件被进行反编译或被轻易地修改。 其作用就是为了保护软件。 常见的大类壳有压缩壳、加密壳、VM 壳的分类。 压缩壳顾名思义就是用来减小软件的文件大小的&#xff1b;加密壳&#xff0c;通过加密软件来保护软件&#xff…

《AI大模型趣味实战》第6集:基于大模型和RSS聚合打造个人新闻电台

《AI大模型趣味实战》第6集&#xff1a;基于大模型和RSS聚合打造个人新闻电台 摘要 本文将带您探索如何结合AI大模型和RSS聚合技术&#xff0c;打造一个功能丰富的个人新闻电台系统。我们将使用Python和PyQt5构建一个桌面应用程序&#xff0c;该应用可以从多个RSS源抓取新闻&…

(学习总结29)Linux 进程概念和进程状态

Linux 进程概念 冯诺依曼体系结构软件运行与存储分级数据流动的理论过程 操作系统操作系统(Operator System) 概念操作系统的功能与作用系统调用和库函数概念 进程概念描述进程 - PCBtask_struct查看进程通过系统调用获取进程标示符 PID通过系统调用 fork 函数创建进程简单使用…

LLM - CentOS上离线部署Ollama+Qwen2.5-coder模型完全指南

文章目录 离线安装OllamaOllama下载Ollama硬件需求Ollama 常用命令参考Ollama安装Ollama 服务管理&开机启动开启局域网访问 Ollama 服务 离线安装模型gguf 文件格式下载Qwen2.5-Coder-7B-Instruct-GGUF格式选择 ( gguf 版本 )构建Modelfile文件加载并运行离线模型测试 集成…

Linux——信号

目录 Linux——信号1.信号的基础了解2.技术应用角度的信号3.产生信号3.1按键组合3.2系统调用产生信号3.2.1 kill()3.2.2 raise()3.2.3 abort() 3.3**.** 软件条件产生信号3.4硬件异常产生信号3.4.1 /0异常3.4.2 内存越界异常 4.理解信号的存在5.总结一下6.核心转储7.全部信号都…

向量叉积的应用——正反画画

1 解题思路 解题思路涉及的向量积相关知识 c实现 #include<iostream> #include<vector>using namespace std;struct TrianglePoint {int x;int y; };int momentForce(TrianglePoint A, TrianglePoint B, TrianglePoint C) {//AB向量&#xff1a;(B.x-A.x, B.y-A.…

构建自定义MCP天气服务器:集成Claude for Desktop与实时天气数据

构建自定义MCP天气服务器:集成Claude for Desktop与实时天气数据 概述 本文将指导开发者构建一个MCP(Model Control Protocol)天气服务器,通过暴露get-alerts和get-forecast工具,为Claude for Desktop等客户端提供实时天气数据支持。该方案解决了传统LLM无法直接获取天气…

Web安全策略CSP详解与实践

引言 &#xff1a;在黑客攻击频发的今天&#xff0c;你的网站是否像“裸奔”一样毫无防护&#xff1f;跨站脚本&#xff08;XSS&#xff09;、数据注入等攻击随时可能让用户数据泄露。今天我们将揭秘一个网站的隐形保镖——内容安全策略&#xff08;CSP&#xff09;&#xff0c…

HC-05与HC-06蓝牙配对零基础教程 以及openmv识别及远程传输项目的概述

这个是上一年的项目&#xff0c;之前弄得不怎么完整&#xff0c;只有一个openmv的&#xff0c;所以openmv自己去我主页找&#xff0c;这篇主要讲蓝牙 这个是我在使用openmv连接单片机1然后单片机1与单片机2通过蓝牙进行通信 最终实现的效果是&#xff1a;openmv识别到图形和数…

点云分割方法

点云分割 通过判断三维距离&#xff0c;实现对创建3团点云的分割 通过判断三维距离&#xff0c;实现对创建3团点云的分割 * 点云1 gen_object_model_3d_from_points (rand(100), rand(100),rand(100), Points1)* 点云2 gen_object_model_3d_from_points (rand(100), 2rand(100…

SpringBoot3使用CompletableFuture时java.util.ConcurrentModificationException异常解决方案

问题描述 在Spring Boot 3项目中&#xff0c;使用CompletableFuture进行异步编程时&#xff0c;偶发{"code":500,"msg":"java.util.ConcurrentModificationException"}异常&#xff0c;但代码中并未直接操作List或CopyOnWriteArrayList等集合类…

细说卫星导航:测距定位原理

测距定位原理 1. 伪距测量技术 核心原理&#xff1a;卫星发射信号&#xff0c;用户接收并记录传播时间&#xff0c;乘以光速得到距离&#xff08;伪距&#xff09;。 技术细节&#xff1a; 信号传播路径分析 信号结构&#xff1a; 卫星信号包含三部分&#xff1a; 载波&…