1.2026金三银四 Android Glide 23连问终极拆解:生命周期、三级缓存、Bitmap复用,大厂面试官到底想听什么?

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自动移到尾部,头部是最久未使用的
// 超限时移除头部

流程图:

graph TD A[访问图片] --> B[移到LinkedHashMap尾部] B --> C{缓存大小超限?} C -->|是| D[移除头部元素
最久未使用]

Q5. Glide为什么要设计"活动缓存"和"内存缓存"两层?它们存和取的顺序如何?

一句话: 避免频繁操作LruCache(要加锁),正在用的放活动缓存,不用的才放LruCache。

存取顺序:

操作 顺序
活动缓存 → 内存缓存 → 磁盘
磁盘/网络 → 内存缓存 → 活动缓存(使用时)

流程图:

graph TD subgraph 读取 R1[开始] --> R2{活动缓存有?} -->|有| R3[直接返回] R2 -->|无| R4{内存缓存有?} -->|有| R5[移出内存→放入活动] end subgraph 释放 L1[图片不再使用] --> L2[从活动移除] --> L3[放入内存缓存] end

核心代码:

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特性复用内存。

流程图:

graph TD A[需要新Bitmap] --> B{池子里有合适的?} B -->|有| C[取出并设置inBitmap] B -->|无| D[创建新Bitmap] C --> E[解码时复用内存] F[图片释放] --> G[放回池子]

核心代码:

java 复制代码
Bitmap reusable = bitmapPool.get(width, height, config);
if (reusable != null) {
    options.inBitmap = reusable;  // 关键!复用内存
    options.inMutable = true;
}

Q8. Glide怎么做大图加载?

一句话: 采样压缩 + 超大图用RegionDecoder分块解码。

流程图:

graph TD A[加载大图] --> B[获取尺寸不加载内存] B --> C{图片超大?} C -->|是| D[RegionDecoder
只解码可见区域] C -->|否| E[采样压缩]

配置:

java 复制代码
Glide.with(context)
     .load(url)
     .override(1080, 1920)  // 限制最大尺寸
     .downsample(DownsampleStrategy.AT_LEAST)

Q9. 它的整体架构是怎么样的?三步骤原理?

一句话: with()绑生命周期 → load()配参数 → into()执行加载。

架构图:

graph TD A[with] --> B[RequestManager
生命周期管理] 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。

设计架构:

graph TD A[API层] --> B[生命周期管理] A --> C[请求构建] B --> D[引擎层] C --> D D --> E[缓存模块
活动/内存/磁盘] D --> F[加载模块
线程池/请求合并] D --> G[解码模块
采样/复用/变换]

核心考虑点:

  1. 生命周期自动绑定(Fragment方案)
  2. 三级缓存(活动+内存+磁盘)
  3. Bitmap复用池(inBitmap)
  4. 多线程池分离(网络/磁盘/解码)
  5. 链式调用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,各自独立缓存,互不影响。

流程图:

graph LR A[400x400原图] --> B[400x400 View] A --> C[200x200 View] B --> B1[Key: URL+400+400
采样率=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定时切换帧。

流程图:

graph TD A[GIF文件] --> B[解析每一帧] B --> C[Handler定时切换] C --> D[刷新View] D --> E{是否循环} -->|是| C

使用:

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 + 进度包装流实现。

实现思路:

  1. 自定义ProgressInputStream包装原始流
  2. 用OkHttp拦截器添加进度监听
  3. 注册自定义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

相关推荐
hhhhhh_we1 小时前
预颜美历:AI驱动的私人面部美学与皮肤全周期管理工具
前端·图像处理·人工智能·python·aigc
Cobyte1 小时前
5.响应式系统比对:手写 React 响应式状态库 Mobx
前端·javascript·vue.js
鹓于1 小时前
PPT VBA随机选题系统实现详解
java·前端·javascript
空中海1 小时前
第九章:安卓系统能力与平台集成
android·数码相机
计算机安禾2 小时前
【数据结构与算法】第50篇:专栏总结:知识图谱梳理与面试高频考点汇总
人工智能·面试·知识图谱
前端双越老师2 小时前
OpenClaw 实战记录:前端 VS 全栈 招聘岗位分析
前端·agent·全栈
Bigger2 小时前
第八章:我是如何剖析 Claude Code 里的“电子宠物”彩蛋的
前端·ai编程·源码阅读
阿拉斯攀登2 小时前
20 个 Android JNI + CMake 生产级示例
android·java·开发语言·人工智能·机器学习·无人售货柜
空中海2 小时前
第十一章:Kotlin 进阶与 Android 原理
android