一、为什么需要 MMKV?SharedPreferences 的痛点
Android 开发中,SharedPreferences
(简称 SP)是常用的轻量级存储工具,但它有三个致命缺点:
-
性能低下:SP 基于 XML 格式存储,每次更新数据都需要全量写入文件。比如修改 1 个字段,需要重写整个 XML 文件,IO 操作频繁。
-
ANR 风险:SP 的
commit()
是同步操作,会阻塞主线程;apply()
虽然异步,但在 Activity 生命周期(如 onStop)时会等待所有写入完成,数据量大时容易引发 ANR。 -
多进程支持差:Android 7.0 后 SP 的多进程模式被废弃,自定义实现复杂且性能低下。
案例:当 APP 需要频繁存储用户操作记录时,SP 可能导致滑动卡顿或启动变慢,而 MMKV 正是为解决这些问题而生。
二、MMKV 是什么?腾讯开源的存储神器
MMKV 是腾讯微信团队开源的高性能 Key-Value 存储框架,核心优势:
-
速度快:写入性能是 SP 的 10 倍以上,读取性能接近 SP。
-
体积小:每个架构的二进制文件小于 60KB,对 APP 体积影响可忽略。
-
多进程支持:基于文件锁实现高效跨进程数据同步,避免 ANR。
-
稳定性强:使用 mmap 内存映射和 Protobuf 序列化,减少数据丢失风险。
核心原理一句话概括:
MMKV 通过mmap内存映射
将文件直接映射到内存,APP 直接操作内存,由系统自动同步到文件;数据采用Protobuf二进制格式
存储,比 XML/JSON 更小更快。
三、MMKV 如何使用?从集成到实战
1. 集成步骤(30 秒搞定)
groovy
// 1. 添加依赖
implementation 'com.tencent:mmkv:1.0.22'
// 2. 初始化(建议在Application中)
String rootDir = MMKV.initialize(this); // 默认路径:filesDir/mmkv
// 3. 获取实例并使用
MMKV kv = MMKV.defaultMMKV(); // 全局实例
kv.encode("user_name", "张三"); // 写入
String name = kv.decodeString("user_name", "默认值"); // 读取
2. 高级用法:多进程与加密
java
// 多进程模式(MODE设为MULTI_PROCESS_MODE)
MMKV multiProcessKV = MMKV.mmkvWithID("shared_data", MMKV.MULTI_PROCESS_MODE);
// 数据加密(AES算法)
MMKV encryptedKV = MMKV.mmkvWithID("secure_data", 0, "1234567890123456"); // 16位密钥
encryptedKV.encode("password", "123456");
3. SP 数据迁移
java
// 将SP数据迁移到MMKV
SharedPreferences sp = getSharedPreferences("old_data", MODE_PRIVATE);
MMKV kv = MMKV.mmkvWithID("new_data");
kv.importFromSharedPreferences(sp);
sp.edit().clear().apply(); // 清空旧数据
四、MMKV 为什么快?核心技术原理解析
1. mmap 内存映射:告别频繁 IO
-
传统 SP 流程:用户空间数据 → 内核空间缓存 → 磁盘,两次数据拷贝。
-
MMKV 流程:通过
mmap
将文件直接映射到内存,APP 直接操作内存,系统自动同步到磁盘,仅一次拷贝。
类比理解:
SP 就像 “先把水倒进杯子(内核缓存),再倒入桶(磁盘)”;
MMKV 则是 “直接操作桶(内存映射),系统自动管理倒水时机”。
2. Protobuf 序列化:更小更快的二进制格式
- SP 用 XML 文本存储,MMKV 用 Protobuf 二进制存储。
例:存储{"name":"张三","age":18}
,SP 的 XML 约 50 字节,MMKV 的 Protobuf 仅约 20 字节。 - Protobuf 采用
Varints
可变长编码,小数值只占 1 字节(如 age=18 仅存0x12
),大数值自动扩展字节数,空间利用率极高。
3. 增量更新:避免全量写入
- SP 更新一个字段需重写整个 XML 文件,MMKV 则将新数据追加到文件末尾,仅在空间不足时触发 “数据重整”(合并重复 Key)。
- 数据重整策略:当文件剩余空间不足时,自动剔除重复 Key,按顺序重新写入,类似 “整理书包,扔掉重复的课本”。
五、多进程同步:文件锁的巧妙设计
MMKV 通过文件锁
解决多进程冲突,比 SP 的 ContentProvider 方案快 10 倍以上:
-
读锁(共享锁) :多个进程可同时读取,如多个页面同时读取用户配置。
-
写锁(排他锁) :仅允许一个进程写入,避免数据冲突。
-
可重入设计:通过计数器记录锁的持有次数,避免递归加锁导致死锁(如 A 进程加锁后又调用内部方法加锁)。
场景示例:
多个进程同时更新用户积分时,MMKV 通过写锁保证只有一个进程能写入,其他进程等待,避免积分数据错乱。
六、性能对比:数据说明一切
操作 | MMKV (ms) | SP (ms) | SQLite (ms) |
---|---|---|---|
写入 1000 次 int | 12 | 119 | 101 |
读取 1000 次 int | 3 | 3 | 136 |
写入 1000 次 String | 7 | 187 | 29 |
读取 1000 次 String | 4 | 2 | 93 |
结论:MMKV 写入性能是 SP 的 10 倍 +,读取性能接近 SP,且多进程场景下优势更明显。
七、使用建议与注意事项
-
适用场景:
- 频繁读写场景(如用户配置、计数器、缓存)。
- 多进程数据共享(如主进程与后台服务通信)。
- 敏感数据存储(配合 AES 加密)。
-
不适用场景:
- 超大文件存储(建议用 SQLite 或文件存储)。
- 复杂数据结构(MMKV 更适合 Key-Value 模式)。
-
最佳实践:
- 用
defaultMMKV()
获取全局实例,避免重复创建。 - 大数据量操作时,主动调用
fullWriteback()
触发数据重整。 - 多进程场景务必启用
MULTI_PROCESS_MODE
。
- 用
八、总结:MMKV 的核心价值
MMKV 通过mmap 内存映射和Protobuf 序列化两大技术,从根本上解决了 SP 的性能瓶颈,同时提供了简单易用的 API 和高效的多进程支持。对于追求性能的 APP(如即时通信、游戏、高频操作工具),MMKV 是替代 SP 的绝佳选择。
一句话总结:MMKV 就像 “存储界的高铁”,用更高效的方式帮你运输和管理数据,告别 SP 带来的 “堵车”(ANR)烦恼。