Android随笔-MMKV 一、MMKV 是什么MMKV 是微信团队开源的一款基于 mmap 内存映射的高性能 key-value 存储组件底层序列化采用 Protobuf支持加密、多进程共享、匿名内存等特性。它在微信内部从 2015 年使用至今后移植到 Android、iOS、macOS、Windows、POSIX 及 HarmonyOS 平台。与 Android 原生的 SharedPreferences 相比MMKV 的核心优势在于维度SharedPreferencesMMKV写入方式全量 XML 写入磁盘增量 append 到 mmap 内存同步机制apply()异步 /commit()同步阻塞无需调用写入即生效序列化格式XML文本体积大Protobuf二进制体积小崩溃安全可能丢失未持久化数据mmap 由 OS 保证刷盘crash 不丢多进程不支持支持文件锁机制性能写 1000 次 int 约 233ms写 1000 次 int 约 4ms二、核心架构分层MMKV 的架构可分为三层Java 层 → JNI 层 → Native 核心层右侧通过 mmap 与文件系统交互。1. Java 层MMKV.java 实现了 SharedPreferences 和 SharedPreferences.Editor 接口对外暴露 encode() / decode() 等方法。所有操作最终通过 nativeHandleNative 层 MMKV 对象的地址透传到 C 层。// Java 层示例MMKVkvMMKV.defaultMMKV();kv.encode(int,Integer.MIN_VALUE);intiValuekv.decodeInt(int);2. JNI 层mmkv_jni.cpp 负责 Java 与 C 的桥接将 jstring 转为 std::string再调用 MMKV::mmkvWithID() 获取或创建 Native 对象。// JNI 方法签名externCJNIEXPORT jlong JNICALLJava_com_tencent_mmkv_MMKV_getMMKVWithID(JNIEnv*env,jobject obj,jstring mmapID,jint mode,jstring cryptKey,jstring relativePath){// ... 转换为 std::string 后调用 MMKV::mmkvWithID()}3. Native 核心层C这是 MMKV 的核心包含以下模块MMKV 类持有 m_dicstd::unordered_map维护内存中的键值对。Protobuf 编解码MiniPBCoder 负责将 m_dic 序列化为二进制或从二进制反序列化。AES 加密可选的 AESCrypt 模块对数据进行 AES 加密。进程锁通过 flock() 或 fcntl() 实现跨进程文件锁保证多进程安全。内存管理ensureMemorySize() 负责空间检查、扩容和内存重整。三、核心机制详解1. mmap 内存映射为什么快mmap 是 Linux 提供的系统调用将磁盘文件映射到进程的虚拟地址空间。此后对内存的读写就等价于对文件的读写无需再经过 read() / write() 系统调用和内核态-用户态的数据拷贝。// MMKV 中的 mmap 调用m_ptr(char*)mmap(nullptr,m_size,PROT_READ|PROT_WRITE,MAP_SHARED,m_fd,0);关键点PROT_READ | PROT_WRITE映射区域可读可写。MAP_SHARED修改会同步回写到文件且对其他进程可见。崩溃安全数据写入 m_ptr 指向的内存后即使 App 立即 crash操作系统也会负责将脏页刷回磁盘数据不会丢失。2. 文件加载与校验loadFromFile()首次获取 MMKV 实例时会调用 loadFromFile() 加载已有数据voidMMKV::loadFromFile(){// 1. 打开文件获取大小m_fdopen(m_path.c_str(),O_RDWR|O_CREAT,S_IRWXU);// 2. 文件大小对齐到页大小整数倍mmap 基于页机制if(m_sizeDEFAULT_MMAP_SIZE||(m_size%DEFAULT_MMAP_SIZE!0)){m_size((m_size/DEFAULT_MMAP_SIZE)1)*DEFAULT_MMAP_SIZE;ftruncate(m_fd,m_size);// 扩容文件zeroFillFile(m_fd,oldSize,m_size-oldSize);// 补零}// 3. mmap 映射m_ptr(char*)mmap(nullptr,m_size,PROT_READ|PROT_WRITE,MAP_SHARED,m_fd,0);// 4. 读取前 4 字节作为 actualSize实际数据长度memcpy(m_actualSize,m_ptr,Fixed32Size);// 5. CRC 校验失败则根据策略恢复或丢弃if(checkFileCRCValid()){// 6. 解密如需要→ Protobuf 反序列化 → 填充 m_dicMMBufferinputBuffer(m_ptrFixed32Size,m_actualSize,MMBufferNoCopy);if(m_crypter)decryptBuffer(*m_crypter,inputBuffer);MiniPBCoder::decodeMap(m_dic,inputBuffer);}// 7. 构造 CodedOutputData指向可写入区域m_outputnewCodedOutputData(m_ptrFixed32Sizem_actualSize,m_size-Fixed32Size-m_actualSize);}文件结构前几个字节| 4 bytes: actualSize | 4 bytes: CRC32 | protobuf 数据 ... |3. 写入流程增量追加AppendMMKV 最大的设计亮点是增量更新。修改一个已有 key 时它不会修改文件中原有位置而是直接在文件末尾 append 一条新记录。读取时只认最后一条。boolMMKV::setDataForKey(MMBufferdata,conststd::stringkey){SCOPEDLOCK(m_lock);SCOPEDLOCK(m_exclusiveProcessLock);checkLoadData();// 确保数据已加载// 更新内存中的 mapm_dic[key]std::move(data);m_hasFullWritebackfalse;returnappendDataWithKey(m_dic[key],key);// 追加到文件}appendDataWithKey() 的核心逻辑boolMMKV::appendDataWithKey(constMMBufferdata,conststd::stringkey){// 计算 key value 在 protobuf 中占用的总字节数size_t sizekeyLengthpbRawVarint32Size(keyLength);sizedata.length()pbRawVarint32Size(data.length());// 确保空间足够不够则触发内存重整if(!ensureMemorySize(size))returnfalse;if(m_actualSize0){// 空文件全量写入整个 mapautoallDataMiniPBCoder::encodeDataWithObject(m_dic);writeAcutalSize(allData.length());m_output-writeRawData(allData);recaculateCRCDigest();}else{// 非空文件增量追加writeAcutalSize(m_actualSizesize);m_output-writeString(key);m_output-writeData(data);// 加密 更新 CRCif(m_crypter)m_crypter-encrypt(ptr,ptr,size);updateCRCDigest(ptr,size,KeepSequence);}returntrue;}4. 内存重整ensureMemorySize()不断 append 会导致文件膨胀、冗余数据堆积。当剩余空间不足时ensureMemorySize() 会触发内存重整Memory ReclamationboolMMKV::ensureMemorySize(size_t newSize){if(newSizem_output-spaceLeft()){// 1. 将当前 m_dic 全量序列化MMBuffer dataMiniPBCoder::encodeDataWithObject(m_dic);size_t lenNeededdata.length()offsetnewSize;// 2. 预测未来空间需求按 2 倍扩容size_t futureUsageavgItemSize*std::maxsize_t(8,(m_dic.size()1)/2);if(lenNeededfutureUsagem_size){do{m_size*2;}while(lenNeededfutureUsagem_size);ftruncate(m_fd,m_size);// 扩大文件zeroFillFile(m_fd,oldSize,m_size-oldSize);munmap(m_ptr,oldSize);// 解除旧映射m_ptr(char*)mmap(m_ptr,m_size,...);// 重新映射}// 3. 全量回写清理冗余writeAcutalSize(data.length());deletem_output;m_outputnewCodedOutputData(m_ptroffset,m_size-offset);m_output-writeRawData(data);recaculateCRCDigest();m_hasFullWritebacktrue;}returntrue;}扩容策略文件大小按 2 倍 增长类似 ArrayList同时预测未来使用量避免频繁扩容。5. 多进程支持文件锁MMKV 通过文件锁实现多进程并发访问读-读多个进程可同时持有读锁并发读取。读-写 / 写-写通过互斥锁保证串行化。锁的实现基于 flock() 或 fcntl()在 loadFromFile() 和写入操作前后加锁/解锁。四、与 SharedPreferences 的对比总结特性SharedPreferencesMMKV存储格式XML 文本Protobuf 二进制写入方式全量写入磁盘增量 append 到 mmap同步调用apply()/commit()无需调用即时生效崩溃安全apply()可能丢失mmap 由 OS 保证多进程❌ 不支持✅ 支持加密❌ 不支持✅ AES 可选体积增量无约 50KB / ABI读取速度解析 XML直接读内存 map五、使用注意事项不要存储过大对象MMKV 适合小数据高频读写单文件过大如 100MB会影响重整性能。key 不宜过长每个 key 都会随 value 一起序列化存储长 key 会浪费空间。多进程模式有开销跨进程锁会带来一定性能损耗非必要不开启。注意内存重整时机频繁修改大量 key 会频繁触发 ensureMemorySize()导致全量序列化开销。迁移成本MMKV 提供了 MMKV.migrateFromSharedPreferences() 方法可平滑迁移旧数据。六、总结MMKV mmap零拷贝内存映射 Protobuf高效二进制序列化 增量追加避免全量写 内存重整自动清理冗余文件锁多进程安全它用操作内存的方式替代了操作文件用增量追加替代了全量覆盖用操作系统刷盘替代了手动同步从而在性能、安全和易用性上全面超越了原生 SharedPreferences。

相关新闻

最新新闻

TuneUp JS高级技巧:如何用screen.js实现复杂UI元素定位

TuneUp JS高级技巧:如何用screen.js实现复杂UI元素定位

TuneUp JS高级技巧:如何用screen.js实现复杂UI元素定位 【免费下载链接】tuneup_js A JavaScript library to ease automated iOS UI testing with UIAutomation and Instruments. 项目地址: https://gitcode.com/gh_mirrors/tu/tuneup_js TuneUp JS是一款强…

2026/7/22 16:58:00
3个核心技巧:深度掌握GT New Horizons的工业自动化与魔法融合

3个核心技巧:深度掌握GT New Horizons的工业自动化与魔法融合

3个核心技巧:深度掌握GT New Horizons的工业自动化与魔法融合 【免费下载链接】GT-New-Horizons-Modpack A big progressive questing modpack for Minecraft 1.7.10 balanced around the mod GregTech. 项目地址: https://gitcode.com/GitHub_Trending/gt/GT-New…

2026/7/22 16:58:00
2026直板夹品牌排行榜:戴森、ghd、雷瓦到底怎么选?

2026直板夹品牌排行榜:戴森、ghd、雷瓦到底怎么选?

直板夹作为日常造型的刚需工具,几乎人手必备。但你会不会遇到这样的情景,打开购物软件一看,几百块到几千块都有,参数表越拉越长——负离子浓度、面板材质、温控精度、浮动结构……越看越不知道怎么选。而直板夹市场也呈现出明显的…

2026/7/22 16:58:00
深夜账户自动进账三万元:一位普通网文创作者的“AI睡后收入”翻倍实录

深夜账户自动进账三万元:一位普通网文创作者的“AI睡后收入”翻倍实录

在绝大多数网文作者还在为每天“日更万字”拼到腱鞘发炎、夜夜卡文脱发的时代,32岁的网文作者林舟,却在临睡前按下了系统运行键。第二天清晨醒来,手机屏幕上赫然亮起数条到账通知:番茄小说稿费结算、起点长尾渠道分账、某爆款微短…

2026/7/22 16:58:00
【AI编程性能分析工具TOP5实战指南】:20年架构师亲测,90%开发者忽略的3个性能盲区

【AI编程性能分析工具TOP5实战指南】:20年架构师亲测,90%开发者忽略的3个性能盲区

更多请点击: https://intelliparadigm.com 第一章:AI编程性能分析工具的演进与核心价值 AI编程正从“能运行”迈向“高效运行”,而支撑这一跃迁的关键基础设施,正是性能分析工具的持续进化。早期工具如 gprof 和 perf 专注于传…

2026/7/22 16:58:00
从无人机视角到数字孪生:如何用CitySim数据集革新交通安全研究

从无人机视角到数字孪生:如何用CitySim数据集革新交通安全研究

从无人机视角到数字孪生:如何用CitySim数据集革新交通安全研究 【免费下载链接】UCF-SST-CitySim1-Dataset Official github page of UCF SST CitySim Dataset 项目地址: https://gitcode.com/gh_mirrors/ucf/UCF-SST-CitySim-Dataset 在自动驾驶技术和智能交…

2026/7/22 16:52:59

月新闻