一、 核心原理分析
1. SharedPreferences (SP)
存储格式: 基于 XML 文件,键值对存储。
I/O 模型: 全量读写。初始化时将整个 XML 加载进内存(Map 结构);修改数据时,即使只改动一个字节,也会将整个 Map 序列化并覆盖写入文件。
线程模型: 读操作同步;写操作提供
commit(同步)和apply(异步)。但apply在 Activity 销毁时会触发同步等待,极易引发 ANR。
2. Jetpack DataStore
存储格式: * Preferences DataStore: 类似于 SP,但不保证类型安全。
Proto DataStore: 使用 Protocol Buffers,支持强类型。
I/O 模型: 流式读写。基于 Kotlin 协程和 Flow 实现非阻塞 I/O。
线程模型: 强制在非 UI 线程执行。通过
Dispatchers.IO调度,底层通过类似队列的机制保证原子性,杜绝了主线程阻塞风险。
3. MMKV (腾讯)
存储格式: 二进制流,使用 Protobuf 序列化。
I/O 模型: mmap (内存映射)。将文件直接映射到逻辑内存地址,读写内存等同于读写磁盘。
性能逻辑: 增量更新。由于 mmap 的特性,MMKV 可以直接在文件末尾追加更新,无需全量重写。
二、 关键特性对比矩阵
三、 深度技术对比
1. 为什么 SP 会引起 ANR?
SP 的 apply() 虽然是异步的,但在 Android 系统中,QueuedWork 会在 Activity 的 onStop()、onPause() 等生命周期方法中调用 wait() 强制等待所有异步写入完成。如果 XML 较大或磁盘繁忙,主线程会因为等待磁盘 I/O 而超时,触发 ANR。
2. DataStore 如何解决一致性?
DataStore 不允许在主线程执行。它利用 Kotlin Flow 的响应式特性,确保了:
原子更新: 每次更新都是事务性的,要么成功,要么失败并保持旧值。
读写隔离: 读操作始终获取最新的磁盘快照。
3. MMKV 为什么这么快?
减少拷贝: mmap 减少了数据从内核空间到用户空间的拷贝次数。
增量读写: 修改一个值只需要在文件末尾追加。只有当文件空间不足时,MMKV 才会触发一次全量的逻辑空间重整(GC)。
四、 选型决策逻辑
场景一:多进程通信
结论: 必须选 MMKV。
理由: SP 在多进程下极易丢失修改;DataStore 目前官方并不支持多进程模式。
场景二:复杂业务逻辑与强类型
结论: 优选 Proto DataStore。
理由: 相比于 K-V 这种松散结构,使用 Protobuf 定义的数据模型在大型工程中更易于维护且具备类型安全性。
场景三:极致响应性能
结论: 首选 MMKV。
理由: 在需要频繁写入(如日志采集、高频状态同步)的场景下,mmap 的性能优势是碾压级的。
场景四:轻量级、无第三方库需求
结论: SharedPreferences 或 Preferences DataStore。
理由: 仅存储简单的几个开关标志,不涉及复杂逻辑。
五、 总结建议
2026 年的 Android 开发中,新项目应彻底废弃 SharedPreferences。
如果项目已经全面 Kotlin 化,推荐使用 DataStore 以获得更好的架构体验。
如果项目存在 多进程、高性能或复杂 K-V 读写 需求,MMKV 是目前工业界的最佳实践。
Android数据持久化(五):SP vs. DataStore vs. MMKV
https://lautung.com/archives/Kr4oiR28