一、 核心原理分析

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 可以直接在文件末尾追加更新,无需全量重写。


二、 关键特性对比矩阵

维度

SharedPreferences

DataStore

MMKV

底层实现

XML + 内存 Map

二进制流 (PB)

mmap + PB

读写性能

低 (频繁全量写磁盘)

中 (异步流式)

极高 (内存操作)

线程安全

弱 (容易产生死锁)

强 (协程互斥)

强 (读写锁)

多进程安全

不支持 (MODE_MULTI_PROCESS 已废弃)

不支持

完美支持 (多进程同步锁)

数据损坏

高 (写入中断文件即损坏)

中 (有备份机制)

低 (系统内核同步保证)

类型安全

是 (Proto DataStore)

包体积

0 (内置)

小 (需要引入相关库)

中 (包含 C++ 库)


三、 深度技术对比

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 是目前工业界的最佳实践。