Glide 资深工程师面试答案(完整23题·精简·核心版)
确保23题完整,每题包含:一句话总结 + 核心要点 + 流程图 + 关键源码
第一优先级:绝对核心(Q1-Q10)
Q1. 为什么用Glide?有什么好处?
一句话: Google推荐的图片加载库,自动绑定生命周期,内存优化好。
核心要点:
- ✅ 页面销毁自动取消加载 → 不内存泄漏
- ✅ 默认RGB_565 → 比ARGB_8888省一半内存
- ✅ 支持GIF/WebP/视频缩略图
- ✅ 三级缓存,滑动不卡
源码:
java
// 默认配置
DEFAULT_MEMORY_CACHE_SIZE = 内存的1/8
DEFAULT_FORMAT = RGB_565 // 2字节/像素,ARGB_8888是4字节
Q2. 如何管理生命周期?
一句话: 添加一个透明的空白Fragment,通过Fragment生命周期回调来控制加载。
流程图:

核心代码:
java
// RequestManagerFragment.java
public void onStart() { lifecycle.onStart(); } // 恢复
public void onStop() { lifecycle.onStop(); } // 暂停
public void onDestroy() { lifecycle.onDestroy(); } // 取消
Q3. glide调用fragment。是否还会创建一个空白的fragment?
一句话:会! 同一个Activity只会创建一个,后续复用。
源码验证:
java
RequestManagerFragment current = fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = new RequestManagerFragment(); // 创建空白Fragment
fm.beginTransaction().add(current, FRAGMENT_TAG).commit();
}
Q4. 缓存机制如何?LruCache原理?
一句话: 三级缓存(活动→内存→磁盘),LruCache基于LinkedHashMap实现最近最少使用淘汰。
三级缓存:
| 缓存 | 实现 | 存什么 |
|---|---|---|
| 活动缓存 | WeakReference | 正在使用的图片 |
| 内存缓存 | LruCache | 最近用但当前没用 |
| 磁盘缓存 | DiskLruCache | 本地文件 |
LruCache原理:
java
// LinkedHashMap第三个参数true = 按访问排序
LinkedHashMap<K,V> map = new LinkedHashMap<>(100, 0.75f, true);
// 每次get/put自动移到尾部,头部是最久未使用的
// 超限时移除头部
流程图:
最久未使用]
Q5. Glide为什么要设计"活动缓存"和"内存缓存"两层?它们存和取的顺序如何?
一句话: 避免频繁操作LruCache(要加锁),正在用的放活动缓存,不用的才放LruCache。
存取顺序:
| 操作 | 顺序 |
|---|---|
| 读 | 活动缓存 → 内存缓存 → 磁盘 |
| 写 | 磁盘/网络 → 内存缓存 → 活动缓存(使用时) |
流程图:
核心代码:
java
// 图片释放时:活动缓存 → 内存缓存
void release(Resource resource) {
activeResources.remove(key); // 从活动移除
cache.put(key, resource); // 放入LRU缓存
}
Q6. 如何节约内存?如何防止OOM?Glide做了哪些内存优化?
一句话: RGB_565 + Bitmap复用池 + 按需采样 + 生命周期回收。
防OOM五板斧:
| 策略 | 效果 |
|---|---|
| RGB_565 | 内存减半(2字节 vs 4字节) |
| BitmapPool | 复用Bitmap,减少GC |
| 按需采样 | 只加载ImageView实际需要的尺寸 |
| 生命周期 | 页面销毁自动回收 |
| LRU淘汰 | 超限自动移除 |
采样压缩代码:
java
// 4000px的图要放到400px的View,采样率=10
int sampleSize = Math.max(inWidth/outWidth, inHeight/outHeight);
Q7. 如何实现图片的复用?
一句话: 通过BitmapPool池子,利用Android的inBitmap特性复用内存。
流程图:
核心代码:
java
Bitmap reusable = bitmapPool.get(width, height, config);
if (reusable != null) {
options.inBitmap = reusable; // 关键!复用内存
options.inMutable = true;
}
Q8. Glide怎么做大图加载?
一句话: 采样压缩 + 超大图用RegionDecoder分块解码。
流程图:
只解码可见区域] C -->|否| E[采样压缩]
配置:
java
Glide.with(context)
.load(url)
.override(1080, 1920) // 限制最大尺寸
.downsample(DownsampleStrategy.AT_LEAST)
Q9. 它的整体架构是怎么样的?三步骤原理?
一句话: with()绑生命周期 → load()配参数 → into()执行加载。
架构图:
生命周期管理] B --> C[load] C --> D[RequestBuilder
参数构建] D --> E[into] E --> F[Engine引擎
缓存+解码+显示]
源码调用链:
java
// 步骤1: 绑定生命周期
RequestManager mgr = Glide.with(activity);
// 步骤2: 构建请求
RequestBuilder builder = mgr.load(url);
// 步骤3: 执行加载
builder.into(imageView); // 触发缓存查询→解码→显示
Q10. 如果让你设计一个图片框架,你会怎么考虑?
一句话: 生命周期管理 + 三级缓存 + Bitmap复用 + 线程池 + 易用API。
设计架构:
活动/内存/磁盘] D --> F[加载模块
线程池/请求合并] D --> G[解码模块
采样/复用/变换]
核心考虑点:
- 生命周期自动绑定(Fragment方案)
- 三级缓存(活动+内存+磁盘)
- Bitmap复用池(inBitmap)
- 多线程池分离(网络/磁盘/解码)
- 链式调用API
第二优先级:进阶核心(Q11-Q17)
Q11. 图片 URL 不变但内容更新,如何让 Glide 重新加载?
一句话: 用signature()加版本号,签名变了缓存Key就变了。
代码:
java
Glide.with(context)
.load(url)
.signature(new ObjectKey(System.currentTimeMillis())) // 时间戳
.into(imageView);
原理:
java
// 缓存Key = URL + 宽高 + 签名 + 变换
signature变化 → Key变化 → 旧缓存失效 → 重新下载
Q12. 2个不同大小的imageView,400400和200200,加载一个大小400*400的图片,流程有什么不一样?
一句话: 生成不同缓存Key,各自独立缓存,互不影响。
流程图:
采样率=1] C --> C1[Key: URL+200+200
采样率=2]
核心: 宽高参与Key生成,所以是两个独立缓存。
Q13. Glide加载一个一兆的图片(100100),是否会压缩后再加载,放到一个200200的view上会怎样,1000*1000呢,图片会很模糊,怎么处理?
一句话: Glide默认不放大,小图放大就是会模糊。解决方案是加载更大的图。
场景分析:
| 原图 | ImageView | Glide行为 | 结果 |
|---|---|---|---|
| 100x100 | 200x200 | 拉伸放大 | ❌ 模糊 |
| 1000x1000 | 200x200 | 采样压缩 | ✅ 清晰 |
解决方案:
java
// 强制加载更大尺寸
Glide.with(context)
.load(url)
.override(400, 400) // 加载大图,让Glide缩小
.into(imageView);
Q14. Glide 缓存 Key 如何生成?
一句话: URL + 宽高 + 签名 + 变换,任一不同则Key不同。
源码:
java
messageDigest.update(url.getBytes()); // URL
messageDigest.update(intToBytes(width)); // 宽度
messageDigest.update(intToBytes(height)); // 高度
signature.updateDiskCacheKey(messageDigest); // 签名
// 所有参数参与生成唯一Key
Q15. 如何加载Gif图片的?
一句话: 自动识别,用GifDrawable + Handler定时切换帧。
流程图:
使用:
java
// 自动识别
Glide.with(context).load(gifUrl).into(imageView);
// 只取第一帧
Glide.with(context).load(gifUrl).asBitmap().into(imageView);
Q16. glide调用在子线程会怎么样?
一句话:会抛异常! into(ImageView)必须在主线程调用。
源码:
java
public ViewTarget into(ImageView view) {
Util.assertMainThread(); // 非主线程抛异常
}
Q17. 磁盘的缓存策略是怎么样的?
一句话: 四种策略,默认ALL(原始图+处理图都缓存)。
| 策略 | 缓存内容 |
|---|---|
| ALL | 原始图 + 处理图 |
| DATA | 只缓存原始图 |
| RESOURCE | 只缓存处理图 |
| NONE | 不缓存 |
java
Glide.with(context)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.DATA) // 只缓存原图
.into(imageView);
Q18. Glide 默认的缓存策略是什么?
一句话: 内存缓存 + 磁盘缓存ALL策略(原始图+处理图都缓存)。
java
// 默认配置
内存缓存大小 = 设备内存的 1/8
磁盘缓存大小 = 250MB
磁盘策略 = DiskCacheStrategy.AUTOMATIC (等价于ALL)
Q19. 如何自定义 Glide 的磁盘缓存路径和大小?
一句话: 通过AppGlideModule自定义配置。
java
@GlideModule
public class MyGlideModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 自定义磁盘缓存路径和大小
builder.setDiskCache(new ExternalDiskCacheFactory(context, "my_cache", 100 * 1024 * 1024));
// 自定义内存缓存大小
builder.setMemoryCache(new LruResourceCache(50 * 1024 * 1024));
}
}
Q20. Glide 的 Transition(过渡动画)有什么用?
一句话: 控制图片从占位符到目标图片的过渡效果。
java
Glide.with(context)
.load(url)
.transition(DrawableTransitionOptions.withCrossFade()) // 淡入淡出
.into(imageView);
Q21. Glide 如何监听加载进度?
一句话: 官方没有直接提供,需要通过自定义ModelLoader + 进度包装流实现。
实现思路:
- 自定义ProgressInputStream包装原始流
- 用OkHttp拦截器添加进度监听
- 注册自定义ModelLoader
Q22. Glide 的 RequestOptions 有哪些常用方法?
一句话: 配置加载参数的核心类,常用方法如下:
java
RequestOptions options = new RequestOptions()
.placeholder(R.drawable.placeholder) // 占位图
.error(R.drawable.error) // 错误图
.override(300, 300) // 指定尺寸
.centerCrop() // 居中裁剪
.circleCrop() // 圆形裁剪
.skipMemoryCache(true) // 跳过内存缓存
.diskCacheStrategy(DiskCacheStrategy.ALL) // 磁盘策略
.priority(Priority.HIGH); // 优先级
Glide.with(context).load(url).apply(options).into(imageView);
Q23. Glide 和 Fresco 对比
一句话: 常规项目用Glide,超大图/低端机用Fresco。
| 特性 | Glide | Fresco |
|---|---|---|
| 包体积 | 较小 | 较大 |
| 内存占用 | 中等 | 极低(5.0以下用Ashmem) |
| GIF支持 | ✅ | ✅ |
| 生命周期绑定 | ✅ 自动 | ❌ 需手动 |
| 学习成本 | 低 | 中等 |
| Google推荐 | ✅ | ❌ |
快速记忆卡片(23题全)
| # | 面试题 | 一句话答案 |
|---|---|---|
| Q1 | 为什么用Glide | 生命周期自动绑定,内存优化好 |
| Q2 | 生命周期管理 | 加透明Fragment |
| Q3 | 会创建空白Fragment吗 | 会 |
| Q4 | 缓存机制/LRU原理 | 三级缓存,LinkedHashMap按访问排序 |
| Q5 | 两层缓存原因 | 避免频繁锁操作 |
| Q6 | 防OOM | RGB_565 + BitmapPool + 采样 |
| Q7 | 图片复用 | BitmapPool + inBitmap |
| Q8 | 大图加载 | 采样压缩 + RegionDecoder |
| Q9 | 整体架构 | with→load→into |
| Q10 | 设计图片框架 | 生命周期+三级缓存+复用+线程池 |
| Q11 | URL内容更新 | signature()加版本号 |
| Q12 | 不同大小View | 缓存Key不同,独立缓存 |
| Q13 | 小图放大模糊 | 会模糊,用override加载大图 |
| Q14 | 缓存Key生成 | URL+宽高+签名+变换 |
| Q15 | GIF加载 | Handler定时切换帧 |
| Q16 | 子线程调用 | 抛异常,必须主线程 |
| Q17 | 磁盘策略 | ALL/DATA/RESOURCE/NONE |
| Q18 | 默认缓存策略 | 内存1/8 + 磁盘ALL |
| Q19 | 自定义缓存路径 | AppGlideModule配置 |
| Q20 | Transition动画 | 占位符到目标图过渡 |
| Q21 | 加载进度监听 | 官方无,需自定义 |
| Q22 | RequestOptions | 占位图/尺寸/裁剪等配置 |
| Q23 | vs Fresco | 常规Glide,超大图Fresco |