Android 本地存储深度对比:SharedPreferences、MMKV、Room 数据库怎么选?
本地存储是 App 的基石,但很多项目里它却是最容易被"随手乱用"的地方。
本文从 原理 → 性能 → 一致性 → 适用场景 四个维度,系统对比 SharedPreferences、MMKV、Room,帮你彻底告别选型焦虑。
一、先给结论:一句话选型指南
| 场景 | 首选方案 |
|---|---|
| 简单配置(开关、主题、引导页) | ✅ SharedPreferences |
| 高频写入 / 跨进程 / 性能敏感 | ✅ MMKV |
| 结构化数据 / 复杂查询 / 事务 | ✅ Room |
| 大文本、JSON、对象集合 | ❌ SP / MMKV → ✅ Room |
| 需要数据迁移、版本管理 | ❌ SP / MMKV → ✅ Room |
二、SharedPreferences:老牌配置存储
1. 工作原理
-
XML 文件存储
-
全量加载到内存
-
通过
Editor.commit()/apply()写入val sp = getSharedPreferences("config", MODE_PRIVATE)
sp.edit().putBoolean("dark_mode", true).apply()
2. 优点
✅ API 简单
✅ 官方原生支持
✅ 学习成本低
✅ 无需额外依赖
3. 致命缺点
❌ 全量读写(哪怕只改一个字段)
❌ apply() 可能 ANR(Activity 暂停时会等待)
❌ 跨进程不安全
❌ 不支持复杂数据类型
❌ 容易引发 GC 压力
apply() 本质是把写磁盘任务丢到 Queue,系统会在 Activity 生命周期切换时强制执行。
4. 典型误用
// ❌ 把用户列表存进 SP
sp.edit().putString("user_list", json).apply()
5. 适用场景
-
开关状态
-
主题模式
-
首次启动标识
-
低频、小数据
三、MMKV:腾讯开源的高性能 KV 存储
1. 核心原理
-
mmap 内存映射
-
Protobuf 二进制协议
-
增量更新
-
跨进程锁
MMKV.initialize(this)
val kv = MMKV.defaultMMKV()
kv.encode("token", token)
2. 性能优势(对比 SP)
| 指标 | SP | MMKV |
|---|---|---|
| 写入速度 | 慢 | 极快 |
| 读取速度 | 中 | 快 |
| 跨进程 | 不支持 | ✅ 支持 |
| ANR 风险 | 高 | 低 |
| 文件大小 | 大 | 小 |
3. 为什么 MMKV 快?
✅ mmap 避免内核态拷贝
✅ Protobuf 紧凑编码
✅ 只写变化数据
✅ 无 GC 压力
4. 优点
✅ 性能碾压 SP
✅ 支持跨进程
✅ 支持加密
✅ API 友好
✅ 微信级验证
5. 缺点
❌ 不适合复杂查询
❌ 不适合关系型数据
❌ 调试不如数据库直观
6. 适用场景
-
Token / Cookie
-
用户偏好
-
高频计数器
-
跨进程数据共享
四、Room:结构化数据的终极方案
1. 架构定位
Room 是 SQLite 的 ORM 封装 ,属于 Jetpack 官方组件。
@Entity
data class User(
@PrimaryKey val uid: Int,
val name: String
)
@Dao
interface UserDao {
@Query("SELECT * FROM user WHERE uid = :id")
suspend fun getUser(id: Int): User
}
2. 核心优势
✅ 编译期 SQL 校验
✅ 强类型
✅ 支持 LiveData / Flow
✅ 支持事务
✅ 内置 Migration
3. 性能特点
| 操作 | 表现 |
|---|---|
| 单条读写 | 中等 |
| 批量写入 | ✅ 优秀 |
| 复杂查询 | ✅ 极强 |
| 随机 KV 访问 | 一般 |
4. 什么时候必须用 Room?
✅ 用户表 / 订单表
✅ 缓存列表数据
✅ 需要排序 / 分页 / 过滤
✅ 需要数据版本升级
5. 缺点
❌ 上手成本高
❌ 简单配置显得"杀鸡用牛刀"
❌ 启动成本略高
五、三大方案横向对比(重点)
| 维度 | SharedPreferences | MMKV | Room |
|---|---|---|---|
| 数据结构 | KV | KV | 关系型 |
| 性能 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 跨进程 | ❌ | ✅ | ✅ |
| 事务 | ❌ | ❌ | ✅ |
| 查询能力 | ❌ | ❌ | ✅ |
| 学习成本 | ⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
| 官方维护 | ✅ | ⚠️(腾讯) | ✅ |
六、真实项目中的最佳实践
✅ 混合使用策略(推荐)
SP → UI 配置、开关
MMKV → Token、用户信息快照
Room → 业务数据、缓存列表
✅ 不推荐的做法
❌ 把 List / Map 序列化成 String 存 SP
❌ 用 MMKV 当数据库
❌ Room 存 boolean 开关
✅ 性能敏感场景示例
登录态管理
MMKV.defaultMMKV().encode("user_id", id)
首页 Feed 缓存
@Dao
@Query("SELECT * FROM feed ORDER BY time DESC LIMIT 20")
fun getFeed(): Flow<List<Feed>>
七、迁移建议
SP → MMKV
MMKV.migrate(userSp)
SP → Room
-
启动时读取 SP
-
写入 Room
-
删除旧 SP
八、总结
没有最好的存储方案,只有最合适的存储方案。
-
SharedPreferences:简单配置的最后一道防线
-
MMKV:性能与效率的首选 KV
-
Room:结构化数据的唯一正解
记住一句话:
能用 SP 的别用 Room,该用 Room 的别硬塞 SP。