第1章 缓存策略整体架构设计
构建一个健壮的移动端缓存系统,首先需要从顶层厘清四个核心问题:选用何种持久化方案、如何调度后台任务、是否坚持离线优先、缓存数据如何分层。本章以 Android 平台为例,但原理适用于 iOS/Flutter 等平台。我们将逐一拆解决策树,并给出推荐组合。
1.1 数据持久化选型对比
移动端持久化选型需权衡 读写性能、查询灵活性、存储体积 和 与 WorkManager 的结合成本。下表列出三种主流方案的关键差异。
| 方案 | 读写性能 | 查询能力 | 存储体积 | WorkManager 适配难度 | 推荐场景 |
|---|---|---|---|---|---|
| Room (SQLite ORM) | 中等(含 ORM 开销) | 强(SQL + DAO) | 数据库文件,随数据增长 | 低(直接插入/查询) | 结构化数据、复杂查询、关联实体 |
| DataStore (Proto/Preferences) | 高(异步、协程) | 弱(仅键值对) | 极小(文件) | 中(需异步转换) | 轻量配置、简单缓存元信息 |
| 文件存储 (JSON/Protobuf) | 低(序列化开销) | 无(全量反序列化) | 大(文本或二进制) | 高(需手动管理读写锁) | 大块数据、离线资源包 |
推荐组合:业务实体(如文章列表)使用 Room;缓存元数据(如最后更新时间)使用 DataStore;静态资源包(如地图瓦片)使用文件存储+Proto。
1.2 WorkManager 的角色定位
WorkManager 不是缓存容器,而是 缓存刷新引擎。其核心职责包括:
- 周期性拉取远程数据,检查缓存是否过期
- 在首次启动或离线恢复后,触发存量缓存的有效性校验
- 约束条件(网络可用、非低电量)确保后台刷新不会影响用户体验
- 链式 Work 组合实现 预取 → 解析 → 持久化 流水线
常见误区:不要用 WorkManager 做缓存数据的实时读取。WorkManager 是调度器,不是数据层;读写缓存应通过 Repository 直接调用 Room/DataStore,WorkManager 只负责通知 Repository “该刷新了”。
1.3 离线优先原则
离线优先(Offline First)要求界面渲染 不依赖网络响应。实现步骤:
- 加载时优先从 本地缓存 返回数据(即使已过期),保证界面不空白
- 后台 WorkManager 同时发起远程请求
- 网络成功 → 更新缓存 → 通过 LiveData/Flow 推送新数据到 UI
- 网络失败 → 保留过期缓存,并显示“可能不是最新”提示(非阻塞)
核心指标:离线优先实现后,用户首次从点击到看到内容应小于 200ms(缓存命中),而不是等待网络到超时。
1.4 缓存层级划分
将缓存分为三层,每层承担不同职责:
| 层级 | 存储方式 | 生命周期 | 清理策略 | 示例 |
|---|---|---|---|---|
| L1 – 内存缓存 | LruCache / Map | App 进程运行期间 | LRU 驱逐 | 当前会话中频繁读取的配置 |
| L2 – 持久化缓存 | Room / DataStore | App 卸载前 | 按时间 TTL 或版本过期 | 用户文章列表、收藏夹 |
| L3 – 资源缓存 | 文件系统 | 可手动清除 / 系统空间不足时 | DiskLRU 或阈值删除 | 图片缓存、离线地图包 |
读取时按 L1 → L2 → L3 顺序逐层穿透,写入时同时更新 L1 和 L2(回写模式)。
实践经验:L2 的 TTL 建议设置为 15~30 分钟(新闻类)或更长(配置类)。L3 资源缓存最大体积应限制在 50MB~200MB,通过 WorkManager 定期清理过期文件。