ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Glide 加载图片请求 到 完整显示

Glide 加载图片请求 到 完整显示 完整流程梳理执行时间Glide.with(this) ↓ 【with()绑定生命周期获取 RequestManager】 根据 Activity / Fragment / Context 获取对应的 RequestManager 让图片请求能够跟随页面生命周期自动暂停、恢复和取消。 ↓ 获取 RequestManager ↓ load(url) ↓ 【load()构建请求记录数据源并根据数据类型确定对应的 ModelLoader】 ↓ 主要是构建请求、确定 ModelLoader、记录数据源 ↓ into(imageView) ↓ 【into()绑定目标 ImageView并真正启动图片加载请求】 ↓ 真正开始执行请求 ↓ Engine.load() ↓ 生成缓存 Key ↓ 查内存缓存 ↓ 内存没有 ↓ 开启 加载图片线程 ↓ 查磁盘缓存 ↓ 磁盘没有 ↓ 网络加载当我们加载图片 请求时Glide.with(context)// 创建 RequestManager管理生命周期.load(url)// 设置数据源url/file/uri/res构建 Request.into(imageView)// ⭐真正触发图片请求第1步生成缓存Key请求发起时执行位置Engine.load()核心逻辑Glide并不会只拿图片URL做Key。它会将URLid、签名signature、宽高width/height、解码器、转换器等10多个参数共同构建成一个唯一的EngineKey对象。目的确保不同尺寸、不同变换效果的同一张网络图在缓存中被视为不同的资源。第2步读取内存缓存LruCache算法区执行位置Engine.load()-loadFromCache()核心逻辑拿着第1步生成的Key去LruResourceCache基于LruCache算法中查找。(LruResourceCashe activeResources也是会在 Glide 的load()阶段 创建ModelLoader对象时创建Glide是单例模式 也是创建Glide 对象时 创建)结果处理命中直接将图片回调给ImageView并显示结束流程。未命中继续下一步。注意从LruCache读取时数据会被暂时移除第3步读取内存缓存弱引用区执行位置Engine.load()-loadFromActiveResources()核心逻辑若LruCache没有则去activeResources弱引用HashMap中查找。这块区域专门存放正在被View使用的图片防止被Lru算法回收。结果处理命中直接回调显示。未命中开启新的加载线程进入第4步。第4步启动子线程优先读取磁盘缓存执行位置EngineRunnable.run()-decode()核心逻辑子线程启动后首先判断是否允许读取磁盘缓存。默认优先尝试从磁盘读取因为网络最慢。第5步读取磁盘缓存先读转换后图片执行位置decodeFromCache()-decodeResultFromCache()核心逻辑使用完整的EngineKey含宽高等参数从Glide自定义的DiskLruCache中查找转换后的图片即压缩/裁剪适配View后的图。磁盘缓存采用的是懒加载第一次磁盘IO时才会初始化以节省开销适用策略DiskCacheStrategy.RESULT或ALL模式下生效。第6步读取磁盘缓存后读原始图片执行位置decodeFromCache()-decodeSourceFromCache()核心逻辑若上一步没找到则使用简化Key仅含URL和signature忽略宽高等参数查找原始尺寸的图片。适用策略DiskCacheStrategy.SOURCE或ALL模式下生效。结果处理磁盘命中解码并返回图片跳至第9步写入内存缓存并显示。磁盘未命中进入第7步网络加载。第7步从网络/源头获取图片资源执行位置decodeFromSource()-fetcher.loadData()核心逻辑通过DataFetcher如HttpUrlFetcher从网络、本地文件或ContentProvider等源头拉取原始图片流。第8步写入磁盘缓存先写原始图后写转换图执行位置decodeFromSource()内部核心逻辑按写入顺序获取到原始图片流后立即写入原始图片到磁盘Key为简化Key。对图片进行尺寸转换/压缩处理。处理完成后写入转换后的图片到磁盘Key为完整EngineKey。限制具体写入哪一类取决于你配置的DiskCacheStrategy。第9步写入内存缓存先写弱引用后写LruCache执行位置EngineJob.handleResultOnMainThread()切回主线程后核心逻辑极其重要图片加载完成后先存入弱引用缓存activeResources此时图片正在被ImageView使用通过acquired引用计数器标记为“活跃”。当图片不再被任何View使用acquired降为0时Glide会自动将其从弱引用区移除并存入LruResourceCacheLru算法区。设计目的正在用的图片防回收不用的图片按最近最少使用原则淘汰。第10步回调显示图片执行位置ResourceCallback.onResourceReady()核心逻辑最终通过into(imageView)将第9步准备好的EngineResource里的Bitmap/Drawable设置到ImageView上完成整个闭环。核心顺序读取顺序内存LruCache → 内存弱引用 → 磁盘转换图 → 磁盘原始图 → 网络写入顺序网络获取 → 写磁盘原始图 → 写磁盘转换图 → 写内存弱引用 → 释放后写内存LruCache你说得对面试官追问“为什么这么设计”才是真正考察深度的环节。单纯背流程确实不够下面我专门从设计动机和底层权衡的角度为你补充这一块的核心逻辑设计思想1. 内存缓存为什么非要拆成“正在使用Active”和“缓存池Lru”防止“抖动”与“频繁回收”如果正在屏幕上显示的图片被放入普通的LruCache当内存紧张时它完全可能被算法回收。但此时用户正盯着这张图回收后必须立即重新解码即使从磁盘读也有IO开销会导致RecyclerView 滑动卡顿或页面闪烁。利用“引用计数”绑定生命周期Glide 通过acquired变量记录图片被 View 持有的次数。大于 0 表示“活着”放在弱引用ActiveResources中既不会被 GC 回收也不会占用 LruCache 的宝贵名额给真正不用的图片腾空间。一旦计数归零界面销毁或滑出屏幕马上降级到LruCache。结论这种设计本质是**“热数据隔离”**确保最高优先级的渲染资源绝对安全同时最大化内存利用率。2. 磁盘缓存为什么非要区分“原始图Source”和“转换图Result”节省 CPU 开销图片加载到 View 通常需要**压缩、裁剪、圆角、滤镜Transform**等操作这些是昂贵的 CPU 计算。默认缓存RESULT转换后意味着下次加载同一张图片时直接跳过解码和变换的步骤极速显示。适配不同尺寸的 View如果缓存只存固定宽高的RESULT当图片要在大图预览和列表缩略图两种场景下显示时尺寸不匹配会导致模糊或拉伸。此时缓存SOURCE原始图就能针对新的宽高重新生成 Result而无需重新下载网络数据在“节省流量”和“节省 CPU”之间做了巧妙平衡。结论这是一种空间换时间 二次加工的思想把“下载”和“处理”解耦让开发者根据 UI 场景ALL/SOURCE/RESULT按需权衡。3. 缓存 Key为什么要把“宽高、变换”等十几个参数都塞进去保证结果的唯一性与正确性同样一张网络图片加载到100x100的圆角 ImageView 和500x500的普通 ImageView处理后的Bitmap对象完全不同。如果不区分这些参数直接返回缓存会导致图片变形、圆角失效或显示错误。签名Signature的防御作用支持开发者放入版本号或文件修改时间。当 URL 不变但服务端图片内容更新时开发者可以通过修改signature强制刷新缓存这是单纯依赖 URL 无法做到的。4. 写入顺序为什么加载完先写磁盘再写内存先写磁盘写入磁盘是 I/O 操作较慢在子线程中完成不影响主线程同时保证即使 App 进程被杀下次启动依然有缓存。后写内存内存写入在主线程或切回主线程后进行需要确保Bitmap已经被完全解码准备妥当。且先写入ActiveResources使用中再通过引用计数降到LruCache完美衔接了“刚加载完正在展示”和“展示完待回收”的生命周期过渡。5. 深度追问为什么不直接用系统提供的 Http 缓存或简单的 LruCache系统的 Http 缓存依赖 Response Header如Cache-Control服务端不可控且无法识别 Android 本地对图片的变换处理。简单的 LruCache只解决内存不够用时的淘汰问题解决不了“图片正在用却被淘汰”的可见性问题也缺乏与磁盘缓存的联动逻辑。Glide 的巧妙通过**“两级内存Active Lru 两级磁盘Source Result”**的矩阵组合本质是将“图片加载生命周期”拆解为使用态、备用态、原始态和加工态在内存抖动、滑动帧率、磁盘空间和网络流量之间找到了最佳的工程平衡点。总结“Glide 的缓存设计不是为了简单存数据而是为了极致地贴合 Android UI 渲染生命周期。它通过引用计数把‘活着’的图片和‘待回收’的图片隔离通过加工前后的分离把‘下载成本’和‘计算成本’解耦。这一切的核心目标只有一个在手机内存严苛的环境下保证用户滑动时绝对不卡顿同时用最少的流量复现最清晰的画面。”
返回列表