深入浅出讲解 MMKV:高效替代 SharedPreferences 的本地存储方案

3 阅读5分钟

一、为什么需要 MMKV?SharedPreferences 的痛点

Android 开发中,SharedPreferences(简称 SP)是常用的轻量级存储工具,但它有三个致命缺点:

  1. 性能低下:SP 基于 XML 格式存储,每次更新数据都需要全量写入文件。比如修改 1 个字段,需要重写整个 XML 文件,IO 操作频繁。

  2. ANR 风险:SP 的commit()是同步操作,会阻塞主线程;apply()虽然异步,但在 Activity 生命周期(如 onStop)时会等待所有写入完成,数据量大时容易引发 ANR。

  3. 多进程支持差: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 次 int12119101
读取 1000 次 int33136
写入 1000 次 String718729
读取 1000 次 String4293

结论:MMKV 写入性能是 SP 的 10 倍 +,读取性能接近 SP,且多进程场景下优势更明显。

七、使用建议与注意事项

  1. 适用场景

    • 频繁读写场景(如用户配置、计数器、缓存)。
    • 多进程数据共享(如主进程与后台服务通信)。
    • 敏感数据存储(配合 AES 加密)。
  2. 不适用场景

    • 超大文件存储(建议用 SQLite 或文件存储)。
    • 复杂数据结构(MMKV 更适合 Key-Value 模式)。
  3. 最佳实践

    • defaultMMKV()获取全局实例,避免重复创建。
    • 大数据量操作时,主动调用fullWriteback()触发数据重整。
    • 多进程场景务必启用MULTI_PROCESS_MODE

八、总结:MMKV 的核心价值

MMKV 通过mmap 内存映射Protobuf 序列化两大技术,从根本上解决了 SP 的性能瓶颈,同时提供了简单易用的 API 和高效的多进程支持。对于追求性能的 APP(如即时通信、游戏、高频操作工具),MMKV 是替代 SP 的绝佳选择。

一句话总结:MMKV 就像 “存储界的高铁”,用更高效的方式帮你运输和管理数据,告别 SP 带来的 “堵车”(ANR)烦恼。