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

Android学习总结之Glide自定义三级缓存(面试篇)

一、三级缓存核心原理与设计
问题 1:为什么需要三级缓存?各层缓存的核心作用是什么?

回答核心

  • 内存缓存:毫秒级快速响应,存储近期浏览的图片(如滑动列表来回切换的图片),通过 LRU 算法自动清理冷数据,通常占内存 15%。
  • 磁盘缓存:持久化存储常用图片,解决内存容量限制,支持离线访问(如商品详情页图片),按 URL 哈希值命名避免重复,容量 100MB 左右。
  • 网络缓存:结合 HTTP 缓存头(如 Cache-Control),避免重复下载,降低流量和服务器压力,容量 50MB,优先存高频访问图片。
  • 分层优势:形成 “速度优先→持久化存储→网络优化” 的三级防护,覆盖 90% 以上的图片加载场景,提升用户体验和系统稳定性。

话术示例
三级缓存的设计是为了解决图片加载中的三个核心问题:

  • 内存缓存(快):就像手机的 “最近使用列表”,存用户刚看过的图片(比如滑动列表时来回切换的图片)。用 LRU 算法自动清理太久没看的图片,比如 8GB 内存的手机分 1.2GB 给它,还会把大图片压缩到屏幕大小,确保滑动时瞬间加载,不卡顿。
  • 磁盘缓存(稳):相当于 “本地相册”,存常用但暂时不在内存里的图片(比如用户常看的商品详情页图)。存 100MB 左右,用 URL 的哈希值命名避免重复,即使手机重启或没网,也能从这里找到图片。
  • 网络缓存(省):类似 “路由器记忆”,避免重复下载。比如用 OkHttp 缓存 50MB,配合服务器的缓存策略(比如设置 1 天有效期),下次打开同一张图直接从路由器拿,省流量还减轻服务器压力。
    三层配合,比如用户滑动列表时,先从内存秒开,滑走后存磁盘,下次没网也能看,联网时优先用网络缓存避免重复下载,覆盖 90% 的使用场景。
问题 2:LruCache 算法的核心数据结构和工作机制是什么?

回答核心

  • 数据结构:双向链表(维护访问顺序)+ 哈希表(快速查找)。
  • 工作机制
    1. 新元素加入链表头部,存在元素访问后移到头部;
    2. 缓存满时删除链表尾部元素(最久未使用);
    3. 通过sizeOf()方法计算元素大小,支持自定义内存占用(如 Bitmap 的字节数)。
  • 大厂优化点:Glide 的 LruResourceCache 结合弱引用队列,感知 Bitmap 回收,避免内存泄漏,同时复用资源池对象减少创建开销。

话术示例
LruCache 就像一个 “智能书架”,核心是按 “最近最少用” 原则管理图片:

  • 每次取图片(访问),就把它放到书架最前面(双向链表头部),很久没取的书(最久未用)放在最后面(链表尾部);
  • 书架满了(内存不够),直接扔掉最后一本书(删除尾部元素),保证书架始终不超载。
    Glide 在这个基础上做了两个关键优化:
  1. 防内存泄漏:给图片加 “弱引用”,就像给书贴一个标签,书被卖掉(系统回收)时,标签自动从书架移除,避免书架上留空标签(无效引用);
  2. 资源复用:把用过的图片暂时存到 “回收箱”(资源池),下次需要相同尺寸的图片,直接从回收箱拿,不用重新买(创建 Bitmap),比如滑动列表时,同尺寸的图片反复用,内存占用能降 30% 以上。
二、常见问题解决方案(缓存穿透 / 雪崩 / OOM)
问题 1:缓存穿透的本质是什么?大厂如何解决高并发下的穿透问题?

回答核心

  • 本质:请求不存在的数据,导致每次都查数据库 / 网络,形成流量黑洞。
  • 解决方案
    1. 布隆过滤器:提前将所有合法 URL 存入布隆过滤器,请求时先过滤无效 URL,拒绝率达 99% 以上;
    2. 缓存空值:对不存在的 URL 缓存一个特殊值(如 NULL),设置短过期时间(5 分钟),防止恶意攻击;
    3. 占位图策略:Glide 中设置error()/fallback()占位图,避免界面闪烁,同时记录无效请求日志。
问题 2:缓存雪崩的危害及多级预防策略?

回答核心

  • 危害:大量缓存同时失效,瞬间流量冲击数据库,可能导致服务雪崩。
  • 预防策略
    1. 错峰过期:给缓存时间添加随机偏移(如 24 小时 ±1 小时),避免集中失效;
    2. 多级缓存:内存 + 磁盘 + CDN 三层缓存,CDN 层抗住 80% 静态资源请求;
    3. 熔断降级:流量突增时,返回低清图或占位图,保证核心流程可用;
    4. 互斥锁:缓存失效时,仅允许一个线程重建缓存,其他线程阻塞等待,避免并发查库。

话术示例
缓存穿透是 “无效请求攻击”,比如恶意用户大量请求不存在的图片 URL,导致每次都要查数据库,就像有人一直按门铃问 “10086 号房在吗”,但小区根本没这个房号。
大厂用三层防线解决:

  1. 门口装 “门禁”(布隆过滤器):提前把所有存在的房号(URL)录入门禁系统,访客(请求)先刷门禁,不存在的直接拦在门外,准确率 99% 以上,比如电商 APP 用布隆过滤器,每天能拦截 10 万 + 无效请求;
  2. 留 “空房记录”(空值缓存):对查过不存在的房号,记下来 “10086 号房不存在”,有效期 5 分钟,期间再有人问直接说 “不用查了”,防止短时间内重复攻击;
  3. 门口贴 “提示牌”(占位图):在 Glide 里设置默认图,比如请求失败时显示 “图片加载中” 的占位图,用户看不到空白,体验更好,同时后台记录这些无效请求,方便定位攻击源。
问题 3:如何从源头预防 OOM?Glide 中的关键配置有哪些?

回答核心

  • 内存优化三原则
    1. 尺寸压缩:按 ImageView 尺寸加载(override(width, height)),避免加载超分辨率图片;
    2. 格式转换:使用 RGB_565(比 ARGB_8888 节省 50% 内存)或 WebP 格式,Glide 中通过format(DecodeFormat.PREFER_RGB_565)配置;
    3. 生命周期绑定:Glide 自动与 Activity/Fragment 绑定,界面不可见时清理资源,避免内存泄漏,同时可手动调用clear(imageView)释放。
  • 进阶策略:动态调整内存缓存大小(如低端机设为 10%,高端机 20%),结合skipMemoryCache(true)跳过非当前屏幕图片的内存存储。

话术示例
OOM 就像 “书包塞满了大书”,图片太大或存太多就会撑爆内存。Glide 通过三个 “瘦身” 技巧预防:

  1. 按尺寸买书:比如手机屏幕是 1000px,就只加载 1000px 的图,不加载 2000px 的原图,用override(1000, 1000)强制压缩,内存占用直接减半;
  2. 选轻便包装:用 RGB_565 格式代替默认的 ARGB_8888,前者每个像素占 2 字节,后者占 4 字节,同样一张图,内存占用少一半,配置代码:
    Glide.with(context).load(url).format(DecodeFormat.PREFER_RGB_565);  
    
  3. 定期断舍离:Glide 会自动跟着 Activity/Fragment 的生命周期清理内存,比如页面关掉时,把相关图片从内存删掉,也可以手动调用clear(imageView),避免 “过期图片” 占空间。
    比如一个短视频 APP,通过这三个技巧,内存峰值能从 500MB 降到 300MB 以下,OOM 崩溃率下降 70%。

话术示例
缓存雪崩就像 “电梯超载”,比如双十一零点,大量商品图片的缓存同时过期,几十万人同时请求,数据库像电梯一样可能被挤瘫。
大厂在大促时会做三件事:

  1. 错峰下班:给每个缓存设置不同的过期时间,比如原定 24 点过期,让有的 23 点 50 分过期,有的 0 点 10 分过期,像员工分批次下班,避免电梯拥挤。代码上可以加随机值:
    int expireTime = 24*60*60 + new Random().nextInt(3600); // 过期时间波动±1小时  
    
  2. 多级防护:最外层用 CDN 缓存(比如阿里云 OSS),扛住 80% 的图片请求,中间层用本地磁盘缓存,最后才到数据库,就像 “保安 + 前台 + 门禁” 三层过滤;
  3. 降级处理:如果流量实在太大,暂时给用户看低清图或模糊图,比如把 10MB 的高清图换成 1MB 的低清图,保证页面能加载,同时对数据库访问加锁,同一时间只允许一个线程更新缓存,其他线程等待,避免所有人同时挤向数据库。
三、HTTP 缓存与网络优化
问题 1:Cache-Control 头中 max-age、no-cache、no-store 的区别和使用场景?

回答核心

  • max-age=3600:缓存有效期 1 小时,期间直接读本地缓存,适合不常更新的图片(如商品主图);
  • no-cache:每次请求需服务器验证缓存有效性(发 304 请求),适合频繁更新但需浏览器缓存的图片(如活动海报);
  • no-store:禁止任何形式的缓存,响应内容不落地,适用于敏感图片(如用户证件照)。
  • 最佳实践:同时配置 ETag 和 Last-Modified,ETag 做精准校验(解决时间戳精度问题),Last-Modified 做快速判断,提升 304 命中率。
问题 2:客户端如何强制获取最新图片?服务器端如何配合?

回答核心

  • 客户端方案
    1. URL 添加版本号或时间戳(如image.jpg?v=2),破坏缓存键一致性;
    2. 设置请求头Cache-Control: no-cache,强制服务器验证;
    3. 清除本地网络缓存(OkHttp 中通过cache.remove(request))。
  • 服务器端配合
    1. 返回正确的 Cache-Control 头(如更新时设置max-age=0);
    2. 图片变更时更新 ETag 值,确保客户端能检测到变化。
四、性能监控与架构设计
问题 1:如何计算缓存命中率?大厂关注哪些核心指标?

回答核心

  • 计算公式:缓存命中率 =(内存命中数 + 磁盘命中数)÷ 总请求数 × 100%。
  • 监控手段
    1. Glide 开启 DEBUG 日志,筛选Fetched from memory cacheFetched from disk cache条目;
    2. 自定义 ModelLoader 统计各层命中次数;
    3. 关注衍生指标:内存峰值(Android Profiler 监控 Heap Size)、加载耗时(System.currentTimeMillis () 打点)、FPS(确保滑动≥55fps)。
  • 优化目标:内存命中率≥30%,磁盘命中率≥50%,整体命中率≥80%。
问题 2:设计一个高并发图片缓存系统,需要规避哪些坑?

回答核心

  • 核心坑点与对策
    1. 热点问题:高频图片集中失效,通过 “热点缓存 + 本地副本” 解决(如 Redis 热 key + 本地 Ehcache);
    2. 存储碎片化:URL 哈希冲突(概率极低),通过加盐哈希或 SHA-256 提升唯一性;
    3. 跨平台一致性:多端(Android/iOS/Web)缓存策略统一,如使用相同的 URL 参数规则和 Cache-Control 配置;
    4. 容量失控:磁盘缓存设置严格的 LRU 淘汰策略 + 过期时间(如 7 天未访问则删除),定期清理僵尸文件。
http://www.xdnf.cn/news/6153.html

相关文章:

  • 网络实验-GRE
  • QT-1.信号与槽
  • HarmonyOS NEXT~React Native在鸿蒙系统(HarmonyOS)上的适配现状与技术展望
  • 【论文阅读】UNIT: Backdoor Mitigation via Automated Neural Distribution Tightening
  • CMD(Command Prompt)和 Anaconda 的不同
  • Mathematics-2025《Semi-Supervised Clustering via Constraints Self-Learning》
  • 查询电脑伪装IP,网络安全速查攻略!
  • 入门OpenTelemetry——可观测性与链路追踪介绍
  • Nginx核心功能及同类产品对比
  • FastByteArrayOutputStream和ByteArrayInputStream有什么区别
  • 基于javaweb的SpringBoot高校图书馆座位预约系统设计与实现(源码+文档+部署讲解)
  • package-lock.json能否直接删除?
  • 【Ansible基础】Ansible设计理念与无代理架构深度解析
  • 2020年下半年试题三:论云原生架构及其应用
  • SAP学习笔记 - 开发11 - RAP(RESTful Application Programming)简介
  • 鸿蒙OSUniApp开发富文本编辑器组件#三方框架 #Uniapp
  • 鸿蒙OSUniApp 制作个人信息编辑界面与头像上传功能#三方框架 #Uniapp
  • 计算机网络的七层“千层饼“
  • TCP/IP参考模型详解:从理论架构到实战应用
  • 牛市买卖数字货币逻辑
  • Java 中序列化和反序列化的详细说明
  • Android学习总结之类LiveData与ViewModel关系篇
  • 【Redis 进阶】分布式锁
  • Q1财报揭示:用户增长与客单价下跌对eBay卖家的蝴蝶效应
  • 最佳实践PPT | 数据架构设计总体规划方案数据中台架构数据架构图解决方案
  • 深度解析智能体:从概念到应用的全方位洞察
  • AI产品上市前的“安全通行证“
  • 7.DTH11和PWM波
  • React系列——nvm、node、npm、yarn(MAC)
  • 机器学习第十讲:异常值检测 → 发现身高填3米的不合理数据