一刻相册 Android 面试准备
基于岗位 JD、com.baidu.youavideo APK 包分析结果,以及当前简历内容整理,目标是帮助你在面试里把“岗位要求、产品理解、个人经历”讲成一条线。
一、先判断这是不是适合你的岗位
1. 岗位核心要求
- 负责 Android 核心功能模块设计与开发
- 参与 Android 应用架构设计,提升复用性和可维护性
- 处理兼容性问题,优化内存、电量、性能、启动速度和流畅度
- 和产品、设计、测试协作推进需求落地
- 熟悉 Kotlin / Java、Android SDK、Retrofit、Glide、RxJava
- 具备组件化、模块化、多线程、UI 渲染优化经验
2. 从 APK 包里看到的产品技术画像
- 这是一个体量比较大的 Android 客户端,不是轻量单体应用
- 架构上是混合栈:
- 传统 Android Activity 体系
- Kotlin-first 模块
- KMP / Compose 增量落地
- 插件化 / 动态模块
- 多进程隔离
- 业务上不只是“相册存储”:
- 云相册
- 智能清理
- OCR / 人物识别 / 分类
- 图片推荐标签、图片搜索
- 图片美化、证件照、AI 宝宝、滤镜等插件能力
- 视频编辑 / 合成链路
3. 你和这个岗位最匹配的地方
- 你长期做 Android 客户端需求交付、上线维护、线上问题修复,和 JD 的“核心功能开发 + 项目推进”是匹配的
- 你简历里有相机、图片、视频、Widget、内存问题、target 升级、稳定性处理,这些和一刻相册的媒体产品属性很贴
- 你在 TrackPack、MagiCam、PicPat、WiseArt 里都能拿出“工程化 + 性能/稳定性 + 业务落地”的案例,能覆盖他们最在意的点
- 你还有工具化意识,这对大团队协作、效率建设和问题治理是加分项
4. 你要主动补强的点
- 如果被问到 KMP / Compose,不要装成深度主导者
- 最好的说法是:
- 我目前主战场还是 Android 原生和 Kotlin 工程落地
- 但我能理解一刻相册这类大型产品为什么会逐步引入 Compose 和 KMP
- 如果团队已经在这条线上推进,我能比较快接住 Android 宿主、模块边界、状态流转和性能治理这部分工作
二、面试里你可以先讲出对产品的理解
1. 一句话判断
- 从包分析看,一刻相册不是单纯的“照片备份 App”,而是“云相册 + 智能整理 + 图片/视频编辑 + AI 能力”的大型媒体产品。
2. 你可以讲的产品技术判断
- 应用有明显的多进程设计,说明它很重视稳定性隔离和任务分治
- 存在
:smart_file_system、:smart_file_system_person、:smart_file_system_ocr、:intelligent_person、:intelligent_ocr 等进程,说明智能整理、OCR、人物识别不是边缘功能,而是核心链路的一部分
- 存在
VideoComposeActivity 和 CustomNvsLiveWindow,说明视频合成/编辑链路比较成熟,且接入了 Meicam 一类的底层媒体能力
- 存在
assets/composeResources、collectAsState、MutableStateFlow、StateFlow,说明产品已经在一些模块里采用 Compose
- 存在
com.mars.united.yike.multiplatform.*、SQLDelight、Ktor 等线索,说明团队在尝试用 KMP 统一部分公共层能力
- 存在
IABTestApi、FlowControl、ServerConfigManager、缩略图日志等,说明他们比较重视实验、流控、线上观测和性能治理
3. 你可以用来体现“提前做过功课”的表达
- 我看下来,一刻相册这边的 Android 形态更像大型媒体客户端,不只是普通业务页堆砌
- 它一方面要承接相册、搜索、清理、识别这些重数据链路,另一方面又有图片/视频编辑和 AI 增值能力,所以对架构、性能和进程治理要求会比一般工具类 App 更高
- 这也是我觉得这个岗位吸引我的地方,因为我过去做过媒体相关模块、性能问题排查和工程治理,比较能对上
三、产品分析里最值得记住的证据点
1. 基础信息
- 包名:
com.baidu.youavideo
- 版本:
6.28.6
minSdkVersion=22
targetSdkVersion=33
Application:com.baidu.youavideo.app.YouaApplication
- 启动页:
com.baidu.youavideo.app.ui.SplashActivity
2. 多进程和智能能力
- Manifest 里可以看到这些关键进程:
:smart_file_system
:smart_file_system_person
:smart_file_system_ocr
:intelligent_ocr
:intelligent_person
:intelligent_classification
:intelligent_face_beautify
:intelligent_similar
- 这说明相册智能整理能力是架构层面的设计,不是单点功能
3. 插件化和动态能力
NPSComponentFactory
assets/nps/manifest/manifest.json
- 插件里能看到:
com.mars.united.plugin.facebeautify
com.mars.united.plugin.idphoto
com.mars.united.plugin.aigc.baby
com.mars.united.plugin.imagefilter
4. 多媒体和视频编辑
VideoComposeActivity
CustomNvsLiveWindow extends NvsLiveWindow
- 说明视频编辑链路不是简单 Web 页,而是原生媒体引擎接入
5. Compose / KMP / 新架构
assets/composeResources/...
CleanToolMainViewKt 中存在 collectAsState
CleanToolScreenViewModel 中存在 MutableStateFlow / StateFlow
com.mars.united.yike.multiplatform.common.storage 下存在 SQLDelight 数据库实现
l21.a 里能看到 Android 宿主给 shared/multiplatform 模块注入 router、network、cloud config、user account 等能力
6. 线上治理和实验
IABTestApi 暴露 config / hitstrategy / flowcontrol
RecordThumbnailLog 说明缩略图链路有专门日志埋点
SmartToolsRepository 里能看到推荐标签、OCR、本地识别、服务端智能工具能力混合协作
四、把你的项目经历映射到这个岗位
1. 最推荐主打的三个项目
PicPat
- 适合回答:
- 内存问题
- 媒体链路
- Widget 复杂场景
- 线上问题治理
- 你可以重点讲:
- Widget 更新导致内存问题是怎么定位和修复的
- 视频压缩或媒体展示链路里你是怎么控制资源占用的
- target 升级、兼容性改造、删除账户等合规工作怎么落地
MagiCam
- 适合回答:
- 相机 / 图像 / OpenGL / Shader
- UI 渲染和性能
- 多媒体业务理解
- 你可以重点讲:
- CameraX + OpenGL 滤镜链路
- 预览与渲染的线程边界
- 如何平衡效果、性能和机型兼容性
TrackPack
- 适合回答:
- Kotlin / MVVM / Jetpack
- WorkManager / Room
- 架构和状态管理
- 你可以重点讲:
- 纯 Kotlin + MVVM 的模块组织
- 延迟任务和调度问题如何治理
- 怎样让数据同步、页面状态和生命周期配合更稳定
2. 次重点可补充的项目
WiseArt
- 能补上 AI 图片业务、Android/iOS 双端协同、埋点实验和商业化流程
Hiyaa.AI
- 能补上需求推进、版本迭代、账号体系、搜索、推送和工具化效率建设
五、90 秒自我介绍建议
版本一:偏岗位匹配
我叫陈帅,做 Android 开发 6 年多,过去几年的工作主要集中在海外工具类和 AI 类产品上,长期负责 Android 端需求交付、版本迭代、上线维护,以及线上稳定性和性能问题处理。
我做过的事情里,和这个岗位比较匹配的有几类。第一类是核心业务开发和项目推进,比如登录、推送、广告、订阅、账号体系、合规改造这些我都长期在负责。第二类是媒体和性能相关的工作,比如我做过相机和图像相关项目,也处理过 Widget 更新导致的内存问题、视频压缩和 target 升级这类偏底层和兼容性的工作。第三类是工程化和效率建设,我会比较主动去做脚本、自动化和工具,把重复工作沉淀下来。
我这次关注一刻相册,主要是因为我觉得它不是简单的相册产品,而是云相册、智能整理、图片视频编辑和 AI 能力结合得比较深的大型客户端。这种产品对 Android 架构、性能和工程落地要求比较高,也比较符合我接下来想持续深挖的方向。
版本二:偏技术一点
我叫陈帅,主要做 Android 客户端开发,6 年多经验,技术栈以 Kotlin / Java、Jetpack、协程、OkHttp / Retrofit 为主,也长期处理线上稳定性、内存和兼容性问题。
过去做过几个和这个岗位比较对口的项目。比如 PicPat 里我处理过 Widget 和媒体相关的线上问题,做过 target 升级和合规改造;MagiCam 里我做过 CameraX + OpenGL/Shader 的相机滤镜方案;TrackPack 是纯 Kotlin + MVVM 架构,涉及 Navigation、Room、WorkManager 这些 Jetpack 组件。我的特点是能比较快接住需求,也愿意往下钻性能、架构和问题定位。
我提前看了一下一刻相册相关的包,感觉这是一个很典型的大型媒体客户端,有多进程、智能清理、OCR、人物识别、视频编辑、A/B 流控和 KMP / Compose 增量落地这些信号。我觉得自己的经历和这个方向还是比较匹配的。
六、面试官大概率会问你的 8 个方向
1. 你怎么看大型 Android 应用的架构设计
你可以怎么答
- 我会先按业务复杂度和演进阶段看,不会一上来就追求特别重的架构
- 如果是像一刻相册这种大型媒体产品,我会重点关注四层:
- 宿主层:Application、进程、路由、登录态、全局配置
- 业务层:相册、清理、搜索、编辑、会员等模块边界
- 基础能力层:网络、图片加载、存储、埋点、实验、权限
- 性能与稳定性层:启动、内存、线程、崩溃、日志、灰度
- 如果团队已经在做 Compose 或 KMP,我会倾向于增量迁移,而不是一次性推翻原有体系
你可以顺手带一句
- 我从这个包里看到他们已经在这么做了,比如 Android 宿主给 multiplatform 模块注入 router、network、cloud config,这就是比较典型的宿主和共享层解耦思路
2. 你做过哪些性能优化
主打回答
- 我做过的优化更多是业务真实场景里的性能治理,而不是只做 benchmark
- 比如 PicPat 里遇到过 Widget 更新带来的内存问题,我会先拆链路看:
- 是图片解码尺寸不合理
- 还是 Bitmap 生命周期过长
- 还是频繁刷新导致对象堆积
- 还是某些机型宿主行为放大了问题
- 另外像启动、页面卡顿、网络请求链路、target 升级后的兼容性抖动,我也都有实际处理经验
面试里要体现的方法论
- 先定指标:启动、内存峰值、卡顿、崩溃、耗电
- 再做分层排查:主线程、I/O、图片、数据库、渲染
- 最后做收益评估:不是只改代码,要看线上效果
3. 如果让你优化一刻相册这类产品的启动速度,你会从哪入手
标准回答
- 这类应用功能多、进程多、SDK 多,启动优化要先分主进程和非主进程
- 我会先做三件事:
- 梳理 Application 启动链路,区分必须同步和可延后初始化
- 做进程隔离,避免每个进程都重复初始化重资源
- 建立启动阶段埋点,拆到首帧前、首帧后、可交互阶段
可以结合包分析说
- 因为我看到它本身就是多进程应用,所以我会特别关注每个进程的初始化边界
- 比如
ProcessTypeImpl 里就有异步预加载、Koin 初始化、Realm 初始化、生命周期监听这些动作,这类产品启动治理的重点往往不是单点,而是初始化总量控制
4. 多进程场景下最容易踩什么坑
你可以答
- 最容易踩的是把多进程当多线程用
- 典型问题包括:
Application 重复初始化
- 单例和静态变量失效
- SharedPreferences 一致性问题
- 数据库或文件并发访问
- 跨进程通信成本和异常处理
- 如果是相册产品,还要额外关注:
- 图片缓存和磁盘清理策略是否跨进程打架
- 识别任务和 UI 进程的状态同步
- 多进程下埋点和实验命中是否一致
5. 你怎么看 Compose 和传统 View 并存
推荐回答
- 我觉得大型存量应用里并存是很正常的,也是比较现实的路线
- Compose 更适合新模块、状态驱动页面和复杂动画
- 传统 View 在一些高复用老页面、成熟控件体系和复杂宿主能力上仍然有价值
- 如果是渐进式落地,我更关注三件事:
- 状态源统一
- 生命周期边界清晰
- 和宿主能力的桥接足够稳定
可以结合产品说
- 一刻相册里清理模块看起来就是 Compose + StateFlow 的方式,而宿主层依然是传统 Android 页面,这种增量式演进我比较认可
6. 如果团队问你对 KMP 的看法
推荐回答
- 我对 KMP 的理解是更适合把“业务无关、平台差异小、复用收益高”的能力抽到 shared 层
- 比如网络、配置、状态模型、存储接口、通用业务规则
- 但和平台深绑定的部分,比如复杂媒体渲染、相机、系统能力接入,还是要保留足够厚的 Android 宿主层
- 所以我不会把 KMP 当银弹,而是看是否真的有双端复用收益、团队是否有持续维护能力
7. 媒体产品里图片/视频链路最难的地方是什么
你可以答
- 难点不在单点 API,而在于要同时兼顾体验、性能、兼容性和资源占用
- 以图片链路为例,要同时考虑:
- 解码尺寸
- 缓存策略
- 滑动场景下的预加载
- 弱网回退
- 大图和超长图处理
- OOM 风险
- 视频链路则要再加上:
- 预览渲染
- 编解码耗时
- 导出时长
- 机型兼容性
- 原生引擎接入稳定性
你可以顺势把 MagiCam 拉进来
- 我之前做相机和图像项目时,对这类问题感受比较深,特别是预览流畅度、滤镜渲染和不同机型表现不一致这些问题
8. 线上出现内存问题或卡顿,你怎么查
回答模板
- 先确认现象:稳定复现还是偶发,集中在哪些机型和页面
- 再分层定位:图片、列表、数据库、主线程、渲染、线程池
- 看证据:日志、埋点、内存曲线、GC 频率、卡顿栈、崩溃栈
- 找根因:对象持有、重复解码、刷新过频、主线程阻塞、锁竞争
- 做修复:代码改动 + 监控补齐 + 防回归验证
你的表达重点
- 不要说“我一般先猜”
- 要说“我会先把链路拆开,再看证据缩小范围”
七、你最值得准备的 5 个真实案例
你要讲清楚
- 现象是什么
- 影响范围是什么
- 你怎么定位到是图片/刷新/生命周期问题
- 你改了哪些点
- 上线后效果如何
面试官最想听的
- 你是不是 owner
- 你是不是只会修 bug,还是能沉淀方法
2. MagiCam 的相机 / 渲染链路
你要讲清楚
- CameraX 和 OpenGL/Shader 在链路里的职责
- 线程和渲染的关系
- 为什么要这么设计
- 兼容性和性能怎么平衡
3. TrackPack 的 Jetpack 架构落地
你要讲清楚
- 为什么选 MVVM + Navigation + Room + WorkManager
- 调度和数据一致性遇到了什么问题
- 你如何做模块划分和状态管理
4. target 升级或兼容性治理
你要讲清楚
- 升级触发背景
- 风险清单怎么做
- 哪些行为变化最麻烦
- 如何和测试、产品一起推进
5. 工具化提升研发效率
你要讲清楚
- 什么问题重复出现
- 你为什么决定做工具
- 工具解决了什么效率问题
- 这个能力如何帮你更好做 owner
八、面试时不要踩的坑
1. 不要把自己说成 KMP / Compose 主导者
2. 不要只说“我做了很多需求”
- 要升级成:
- 我负责的模块是什么
- 技术挑战是什么
- 我如何判断和推进
- 最终带来什么结果
3. 不要把性能优化说成“优化了很多”
4. 不要被带着只聊八股
- 这次最该主动往“媒体产品、性能、架构、稳定性、工程治理”方向带
九、你可以反问面试官的问题
建议反问 1
- 一刻相册 Android 这边现在更核心的挑战,是偏业务迭代、性能稳定性,还是架构演进?
建议反问 2
- 目前 Compose 或 KMP 在团队里的使用范围大概到什么程度,是新模块增量接入,还是已经有比较成熟的 shared 层了?
建议反问 3
- 媒体和智能整理相关能力,Android 侧更多是负责端上性能与体验,还是也会参与到算法能力接入和链路治理?
建议反问 4
- 团队在启动速度、内存、卡顿、耗电这些指标上,目前最关注的是哪几个?
十、临场速记版
面试主线
- 这是一个大型媒体客户端
- 我有 Android 核心业务交付经验
- 我做过媒体/图像/性能/稳定性相关工作
- 我能接住复杂模块,也愿意做工程治理
你要反复强调的关键词
- Kotlin / Java
- 架构设计
- 模块边界
- 性能优化
- 稳定性
- 兼容性
- 媒体链路
- 线上问题定位
- owner 意识
最后一句总结
- 如果这轮面试要抓一个核心方向去准备,优先顺序建议是:
性能与稳定性案例
媒体/图像项目经历
大型客户端架构理解
多进程与启动优化
Compose / KMP 的理性理解
十一、一刻相册里 Glide 是怎么用的
这一部分很适合拿来回答两个问题:
1. 你怎么看媒体产品里的图片加载设计
2. 你对 Glide 的理解是不是停留在业务 API 层
1. 先说结论
- 一刻相册不是业务层直接裸用
Glide.with().load()
- 它是把 Glide 放在自研图片加载框架下面,Glide 更像底层执行引擎
- 上层自己做了:
- URL 规则和请求封装
- 双 URL 缩略图 / 大图策略
- 鉴权下载
- 跨进程参数同步和预加载
- 缓存探测
- 统计和日志
2. 你可以怎么一句话讲给面试官
- 我看一刻相册这边对 Glide 的使用不是简单调用,而是做成了一层平台化图片能力
- Glide 负责请求、解码、缓存和 target 生命周期
- 但网络鉴权、缩略图策略、预加载、统计和跨进程协作,基本都被它自己的 imageloader 框架接管了
3. 它的架构分层可以怎么理解
第一层:业务调用层
- 业务侧大多走的是统一封装:
DrawableImageLoaderKt.loadDrawable(...)
BitmapImageLoaderKt.loadBitmap(...)
- 这说明团队不希望业务到处直接拼 Glide 请求
- 好处是:
- 统一 cache strategy
- 统一 placeholder / error / transform
- 统一硬件位图开关
- 统一埋点和错误处理
第二层:请求封装层
- 原始 URL 不会直接丢给 Glide
- 会先转成
RequestUrl,有些场景还会转成 Pair<RequestUrl, RequestUrl>
- 这个
Pair 基本可以理解成:
- 第一张:更轻量的缩略图 / efficiency 图
- 第二张:最终展示图
第三层:Glide 执行层
- 真正执行请求的是
GlideImageLoaderKt
- 它负责:
- 组装
RequestOptions
- 应用缓存策略
- 应用 Transform
- 选择普通加载还是 efficiency 双图加载
- 绑定 target / listener
第四层:网络与缓存层
- 网络层不是 Glide 默认实现,而是自定义
ModelLoader + DataFetcher
- 缓存层除了使用 Glide 自带缓存,还会主动探测磁盘缓存是否命中
- 这说明团队很重视“加载前决策”,不是盲目发请求
4. 最重要的设计:双 URL 效率加载
它在做什么
- 一刻相册会在某些场景下先加载一张更轻量的小图,再补一张更完整的大图
- 这样做的目标通常是:
- 列表滑动时先保证首屏速度
- 弱网下先让用户看到内容
- 降低直接加载大图造成的卡顿和内存抖动
代码上怎么体现
loadDrawable 先拿到 requestUrlPair
GlideImageLoaderKt.q(...) 里会判断是否开启 efficiency
- 如果满足条件,就走
y(...)
- 如果不满足,就直接走普通加载
u(...),同时调用 preLoad(...)
面试里你可以怎么讲
- 这个设计本质上是在“体验优先”和“资源成本”之间找平衡
- 对媒体类产品来说,用户感知往往不是最终清晰图什么时候到,而是列表是不是立刻有内容、滑动会不会抖、弱网会不会白屏
- 所以这类缩略图优先、大图补齐的策略很常见,但一刻相册做得更深入,因为它把缓存探测、预加载和统计也接进去了
5. 它怎么扩展 Glide 网络层
关键点
- 自定义
DataFetcher<InputStream>
- 底层走
OkHttpClient
- 请求头会补:
Cookie: STOKEN=...; BDUSS=...
User-Agent
Accept-Encoding
这说明什么
- 这类产品的图片资源不是完全匿名公共资源
- 图片下载链路和账号态、服务端鉴权是绑定的
- 所以直接用 Glide 默认 URL 加载能力不够,必须有一层自定义网络接入
还有两个加分点
- 非主进程会通过
ImageLoaderProvider 去拿 ImageRequestParam
- 失败会根据错误类型做最多 3 次重试
6. 它怎么做缓存策略
不只是用 Glide 默认缓存
- 它会主动判断:
- 原图缓存是否存在
- 变换后资源缓存是否存在
- 当前 key 是否在加载中
这样做的价值
- 可以在请求发出去之前决定:
- 要不要走 efficiency 小图链路
- 要不要直接命中缓存
- 要不要提前 preload
面试表达
- 这说明他们不是把 Glide 当黑盒,而是深入到了缓存 key 和资源缓存决策层
- 这种思路在大型图片产品里很重要,因为真正影响体验的,不只是“能不能加载”,而是“是不是用最低代价拿到用户当前最需要的图”
- 业务层不是直接传 Glide 的 transform
- 而是传自定义的
Transform
- 再映射成:
FitCenter
CenterCrop
CircleCrop
RoundedCorners
Blur
Bitmap 配置
- 支持统一控制:
ARGB_8888
RGB_565
- 是否允许 hardware bitmap
这部分你可以怎么讲
- 这类统一抽象的好处是业务解耦
- 后面如果底层图片引擎切换,或者想做更细的配置治理,业务层改动会小很多
8. 它怎么做 Target、Listener 和统计
自定义 target
- 有
ImageViewTarget
- 有
BitmapViewTarget
- 有
EfficiencyImageViewTarget
这些 target 不只是为了显示图片
- 还会负责:
- 生命周期安全
- 成功 / 失败回调
- 首次成功数据源记录
- bitmap 大小日志
- 统计上报
- 清理或取消 efficiency 任务
说明
- Glide 在这里不只是“拿图工具”
- 它已经是完整监控链路的一部分
9. 你在面试里可以主动总结成三句话
- 第一,这个产品对 Glide 的使用是平台化封装,不是简单业务调用
- 第二,它的核心亮点是双 URL 效率加载、自定义鉴权下载、缓存探测和跨进程预加载
- 第三,这说明团队对图片链路的关注点已经从“能显示”升级到“体验、性能、稳定性和可观测性”
10. 面试官如果追问,你可以这样答
如果问:你觉得这里为什么要包一层,而不是业务直接用 Glide
- 因为大型产品如果业务直接使用 Glide,很快会出现缓存策略不统一、transform 不统一、鉴权逻辑散落、埋点缺失、生命周期处理不一致的问题
- 包一层之后,业务只表达“我要什么图”,底层框架统一处理“怎么高效安全地拿到图”
如果问:这里最体现团队水平的点是什么
- 我觉得不是自定义 GlideModule 本身,而是他们把图片加载当成了平台能力
- 尤其是双 URL 策略、缓存探测、跨进程协作、统计和日志,这些都说明团队是在从全链路角度设计图片系统
如果问:你能从这里学到什么
- 我会重点吸收三点:
- 业务层和图片框架解耦
- 先做缓存和链路决策,再发请求
- 把图片加载纳入可观测体系,而不是只看显示结果
十二、Glide 源码常考知识点复习
这一部分建议你按“整体架构 -> 请求流程 -> 缓存 -> 生命周期 -> 扩展点”去记,不要只背类名。
1. 先背一版总览
一句话
- Glide 是一个以图片请求、资源复用、分级缓存和生命周期管理为核心的图片加载框架。
你可以展开成五个关键词
- 请求管理
- 生命周期绑定
- 多级缓存
- 解码与复用
- 扩展能力
2. Glide.with() 到底做了什么
常见问法
Glide.with(this).load(url).into(imageView) 背后做了什么
推荐回答
Glide.with(...) 先根据传入的是 Context / Activity / Fragment / View,找到对应的 RequestManager
RequestManagerRetriever 会决定绑定哪个生命周期
load(...) 会把 model 包装到请求构建器里
into(imageView) 时才真正开始构建并发起请求
- 请求会进入
RequestBuilder -> Request -> Engine 的执行链路
你要强调的点
- Glide 不是
load() 就马上发请求
- 真正触发通常在
into() 或 submit() 这类终点方法
3. Glide 为什么能自动跟生命周期绑定
核心类
RequestManager
RequestManagerRetriever
Lifecycle
RequestTracker
SupportRequestManagerFragment / RequestManagerFragment
原理
- 当你传入
Activity / Fragment 时,Glide 会插入一个隐藏 Fragment
- 这个隐藏 Fragment 感知宿主生命周期
- 它把
onStart / onStop / onDestroy 传给 RequestManager
RequestManager 再去暂停、恢复、清理请求
面试里的标准说法
- Glide 的生命周期管理本质不是靠 ImageView 自己感知,而是通过隐藏 Fragment 挂到宿主生命周期上,再统一管理请求状态
4. Glide 的请求主流程
推荐记忆顺序
Glide.with() 拿到 RequestManager
load() 生成 RequestBuilder
into() 创建 Target 和具体 Request
Request 交给 Engine
Engine 先查缓存,再决定是否起解码任务
- 解码完成后回调
Target
关键类
RequestBuilder
SingleRequest
Target
Engine
EngineJob
DecodeJob
面试可以这样概括
SingleRequest 负责单次请求状态机
Engine 负责资源复用和缓存协同
EngineJob 负责把多个相同请求合并等待同一个结果
DecodeJob 负责真正的资源解码流程
5. Glide 的多级缓存一定要会
先背结论
- Glide 常说的缓存顺序一般是:
- Active Resources
- Memory Cache
- Resource Disk Cache
- Data Disk Cache
- Source
每一级是干什么的
Active Resources
- 当前正在被界面使用的资源
- 通常是弱引用管理
- 避免同一资源在活跃展示时被重复解码
Memory Cache
- 进程内 Lru 内存缓存
- 提升二次展示速度
Resource Disk Cache
- 缓存变换后的资源
- 比如已经裁剪、圆角、缩放后的结果
Data Disk Cache
- 缓存原始数据
- 比如原始网络图片字节流
Source
- 内存和磁盘都没有命中,才去本地文件或网络源头拿数据
高频追问:为什么分 Data 和 Resource 两级磁盘缓存
- 因为有些场景更适合复用原始数据重新做变换
- 有些场景更适合直接复用最终变换后的结果
- 分开以后可以兼顾灵活性和性能
6. Engine、EngineJob、DecodeJob 各自职责
Engine
- 图片加载总调度中心
- 负责查缓存、复用 in-flight 请求、启动解码任务
EngineJob
- 负责一个资源加载任务的回调分发
- 如果多个地方同时请求同一资源,会挂到同一个
EngineJob 上等待结果
- 这样可以避免相同资源重复解码、重复网络请求
DecodeJob
- 真正执行解码
- 会按阶段跑:
- 资源缓存
- 数据缓存
- 源数据
- 还会处理变换、转码、编码回缓存
面试答法
- 我会把它理解成:
Engine 负责调度
EngineJob 负责并发合并和结果派发
DecodeJob 负责实际干活
7. Glide 为什么能减少 OOM
常见回答点
- 按目标尺寸解码,不是默认全尺寸原图解码
- Bitmap 复用,减少频繁创建大对象
- 有内存缓存和活跃资源复用
- 支持
RGB_565、ARGB_8888 等格式控制
- 生命周期绑定,页面退出后及时取消请求和释放资源
高频追问:BitmapPool 是什么
BitmapPool 是 Glide 的 Bitmap 复用池
- 已经释放但可复用的 Bitmap 不直接交给 GC,而是按条件放回池中
- 后续解码如果尺寸和配置合适,就优先复用池里的 Bitmap
- 这样能减少频繁申请和回收大块内存带来的抖动
8. 内存缓存和 BitmapPool 有什么区别
标准答案
- Memory Cache 存的是“可直接展示给业务的资源”
- BitmapPool 存的是“底层可复用的 Bitmap 内存块”
- 一个偏业务结果复用
- 一个偏底层内存复用
9. Glide 的磁盘缓存策略常考点
DiskCacheStrategy
ALL
- 原始数据和转换后资源都缓存
NONE
- 不做磁盘缓存
DATA
- 只缓存原始数据
RESOURCE
- 只缓存变换后资源
AUTOMATIC
- Glide 自动根据数据源判断
怎么选
- 远程网络图且可能有多种变换时,常会考虑
DATA
- 列表里固定尺寸固定变换的场景,
RESOURCE 或 ALL 更可能带来展示收益
- 不是缓存越多越好,要结合磁盘占用和命中模式
10. Glide 的 key 设计为什么重要
你要记住
- 缓存命中不是只看 URL
- Glide 的 key 往往还会受这些因素影响:
- model
- signature
- width / height
- transformation
- options
- 资源类型
高频追问:为什么同一个 URL 有时命不中缓存
- 因为尺寸、变换、signature 或 option 变了,最终 key 就变了
- Glide 缓存命中的本质是“请求语义一致”,不是字符串 URL 一样就够了
11. Glide 的扩展点一定要会
高价值扩展点
AppGlideModule / LibraryGlideModule
ModelLoader
DataFetcher
Transformation
Target
RequestListener
各自用途
AppGlideModule
- 全局配置入口
- 比如内存缓存、磁盘缓存、日志级别、解码格式
ModelLoader
- 决定怎么把某种 model 转成可加载的数据
DataFetcher
- 负责真正取数据
- 可以来自网络、文件、数据库、content uri
Transformation
- 图像变换
Target
- 结果交付目标
RequestListener
- 请求过程监听
面试里可以顺手说
- 一刻相册的图片加载设计里,最明显用到的就是 GlideModule、ModelLoader、DataFetcher、Target 和 Listener 这些扩展点
12. Glide 的线程模型怎么理解
简化理解
- 主线程负责发起请求、状态切换、结果回调到 View
- 后台线程负责磁盘读取、网络获取、图片解码和变换
高频追问:为什么图片解码不能放主线程
- 因为解码和变换都可能很重,容易阻塞 UI,导致掉帧和 ANR
- Glide 把这些重活都放到了后台执行,再把结果切回主线程交付
Decode
- 把原始数据解码成资源对象,比如
InputStream -> Bitmap
Transcode
- 把一种资源类型转成另一种资源类型
- 比如
Bitmap -> Drawable
14. preload() 和 into() 的区别
into()
- 目标是展示到某个 View
- 有生命周期和 UI 绑定
preload()
- 目标是提前把资源放进缓存
- 不一定立刻显示
- 更适合列表预加载、即将进入页面的资源预热
面试场景
- 如果问你列表图片优化怎么做,可以答:
- 合理尺寸
- 合理缓存策略
- 滑动场景预加载
- 避免同图重复请求
15. Glide 为什么不用业务自己管理线程和缓存
推荐回答
- 因为图片加载不是单次 I/O 问题,而是请求合并、生命周期、缓存命中、尺寸控制、资源复用和解码调度的综合问题
- 如果业务自己零散实现,复杂度和出错率都很高
- Glide 的价值就在于把这套复杂性沉淀成框架能力
16. Glide 常见面试题速答
问:Glide 和 Picasso / Coil 的核心区别你怎么理解
- Glide 更强调资源复用、BitmapPool、多级缓存和复杂图片场景性能治理
- Coil 更 Kotlin 化,和协程、现代 Android 生态结合更自然
- Picasso 相对轻量,但在复杂媒体场景下能力没有 Glide 那么重
问:为什么列表里还是会闪烁或错位
- 常见原因是:
- View 复用时旧请求没取消
- 占位图策略不合理
- 尺寸不稳定
- 同一 View 在不同绑定时 key 不一致
- 动画或 crossfade 用得不合适
问:什么时候会 OOM
- 大图按原尺寸解码
- 同屏图片过多
- 变换链路重复创建大 Bitmap
- 生命周期没绑好导致页面退出后请求还在跑
- 缓存配置和业务场景不匹配
17. Glide 源码复习建议
如果你时间不多,优先看这条主线
Glide.with() 如何拿到 RequestManager
into() 如何触发 SingleRequest
Engine 如何查缓存
EngineJob / DecodeJob 如何分工
ActiveResources / MemoryCache / DiskCache 如何协作
BitmapPool 为什么能减少抖动
AppGlideModule / ModelLoader / DataFetcher 怎么扩展
如果面试前只够背一段
- Glide 的核心不是 ImageView 展示,而是围绕生命周期、缓存、资源复用和解码调度,帮业务把图片请求的复杂性收敛起来
- 真正的源码重点是 RequestManager、SingleRequest、Engine、EngineJob、DecodeJob、多级缓存和扩展点
十三、Glide 这块你怎么准备最划算
1. 必须会讲的
- Glide 生命周期绑定原理
- Glide 多级缓存
- BitmapPool 和 MemoryCache 区别
- Engine / EngineJob / DecodeJob 分工
- 常见扩展点
2. 结合一刻相册必须会讲的
- 为什么要包一层 imageloader
- 为什么要做双 URL 效率加载
- 为什么要自定义 DataFetcher
- 为什么要做缓存探测和预加载
- 为什么 target / listener 里要做统计和日志
3. 你最推荐的答题策略
- 先讲 Glide 通用原理
- 再讲一刻相册里的落地方式
- 最后再联系你自己的项目经验
4. 一段可以直接背的总结
- 我对 Glide 的理解不只是业务 API 层面。它本质上是一个围绕请求管理、生命周期、多级缓存、资源复用和解码调度设计的图片框架。结合一刻相册这类媒体产品来看,真正有价值的不是简单把图显示出来,而是通过 Glide 的扩展点,把鉴权下载、缩略图优先、缓存探测、预加载、统计监控这些能力整合成统一的图片加载平台。这也是我接下来准备重点加强的一块。
十四、Glide 面试速记图
这一部分适合面试前 5 分钟快速过一遍。不要死记类名,重点记住调用链路和每层职责。
1. Glide 主流程速记图
业务调用
Glide.with(context)
.load(model)
.into(target)
│
▼
1. 找请求管理者
RequestManagerRetriever
│
▼
RequestManager
│
▼
2. 构建请求
RequestBuilder
│
▼
SingleRequest
- 记录本次请求状态
- 协调 placeholder / error / target / listener
│
▼
3. 进入引擎层
Engine
- 先查缓存
- 合并相同请求
- 决定是否启动解码
│
├── ActiveResources
├── MemoryCache
├── Resource Disk Cache
├── Data Disk Cache
└── Source(文件/网络/内容提供者)
▼
4. 如果缓存没命中
EngineJob
- 管理同一个资源的并发请求
- 多个请求等同一个结果
│
▼
DecodeJob
- 真正干活
- 按阶段尝试:
ResourceCache -> DataCache -> Source
- 解码、变换、转码、写缓存
│
▼
5. 数据加载链路
ModelLoader<Model, Data>
│
▼
DataFetcher<Data>
│
▼
ResourceDecoder<Data, Resource>
│
▼
Transformation<Resource>
│
▼
Transcoder<Resource, Transcode>
│
▼
6. 回到主线程交付结果
Target / ImageView / CustomTarget
RequestListener
2. 一版更短的背诵版
with() 找 RequestManager
load() 配置 model
into() 创建 SingleRequest
Engine 查多级缓存
没命中就走 EngineJob + DecodeJob
底层链路是:
ModelLoader -> DataFetcher -> Decoder -> Transform -> Transcoder
最后回调 Target
3. Glide 扩展点速记图
全局配置:AppGlideModule
数据模型接入:ModelLoader
真正取数据:DataFetcher
数据解码:ResourceDecoder
图片处理:Transformation
结果交付:Target / CustomTarget
过程监听:RequestListener
缓存控制:signature / DiskCacheStrategy / skipMemoryCache
4. Glide 缓存速记图
ActiveResources
↓
MemoryCache
↓
Resource Disk Cache
↓
Data Disk Cache
↓
Source
5. 面试时最推荐背的 5 句话
- Glide 的核心不是显示图片,而是请求管理、生命周期、多级缓存和资源复用。
with() 本质上是在找和宿主生命周期绑定的 RequestManager。
Engine 是总调度中心,EngineJob 负责并发合并,DecodeJob 负责真正解码。
DataFetcher 的泛型不固定,关键是后面要有对应的 Decoder。
- Glide 的高价值扩展点是
AppGlideModule、ModelLoader、DataFetcher、Transformation、Target、RequestListener。
6. 结合一刻相册的答题速记
业务层
→ 统一 imageloader 封装
→ GlideImageLoaderKt
→ 自定义 GlideModule
→ 自定义 ModelLoader/DataFetcher
→ 双 URL efficiency 加载
→ 缓存探测 + 预加载
→ Target/Listener 做统计和日志
7. 最后一段总结
- 如果面试官让我快速总结 Glide,我会说它本质上是一个围绕请求管理、生命周期、多级缓存、资源复用和解码调度设计的图片框架。对于一刻相册这种媒体产品,真正的价值不只是把图显示出来,而是通过 Glide 的扩展点把鉴权、缓存、预加载、统计和效率加载这些能力统一收敛到框架层。