MMKV源码解析

基于version:1.2.5

目录

1、初始化

2、获取MMKV实例

1、MMKV内部构建

3、写入:encode

在缓存中找到了

4、读取:decode

5、其他:多进程、文件锁等

1、初始化

链路:MMKV.java:initialize->jniInitialize→native-bridge.cpp:initializeMMKV->MMKV.cpp:initializeMMKV

  1. 初始化经过MMKV.java文件进行了根目录的记录随后调用到了初始化native层
  2. native层:
  • 通过Linux系统的互斥锁、自己设置的标记为来保证:一个进程只初始化一次MMKV
  • 保存根目录为全局变量
  • 通过传入的串地址生成真正目标需要的目录结构

2、获取MMKV实例

链路:MMKV.java:mmkvWithID->getMMKVWithID->native-bridge.cpp:mmkvWithID->MMKV_Android.cpp:mmkvWithID

注⚠️:不管是使用默认的defaultMMKV还是mmkvWithID都是最终都汇于一处

  1. 前部的调用都是通过是否加密、realPath条件进行不同的参数配置最终都是调用到了MMKV_Android.cpp的方法中
  2. MMKV_Android.cpp的mmkvWithID:
    • 通过mmapID和relativePath拼接后md5得到mmap的key(项目中其实relativePath为null,所以mmapId就是我们传入的fileName)
    • 缓存中找到直接返回
    • 如果缓存没有就构建一个新的实例

其实从这里可以发现java层的MMKV实例只是一个壳,是否构建MMKV实例是在native层进行的

1、MMKV内部构建

在MMKV_Android.cpp进行了真正的MMKV构建,各种参数配置,这里我们关注两个最关键的地方:m_metaFile、loadFromFile

文件映射:m_metaFile

链路:MMKV_Android.cpp:MMKV:m_metaFile:MemoryFile_Android.cpp:MemoryFile:reloadFromFile:mmap

首先验证文件大小

  • 文件小于最小单位或者不是最先文件单位的整数倍,则进行整理大小(处理成整数倍方便对应内存映射管理)
  • 大小符合映射条件,直接进行mmap映射

我们来来看下第一种情况:

第二种情况省去了第一种情况的大小调整直接进行了映射

到这里文件就完成了通过mmap映射到内存,并拿到了映射的地址:m_ptr

载入数据:loadFromFile

映射完内存,把数据文件倒入到内存就可以开干了!

导入数据前会现检验一下是否映射完毕映射内存可用,否则会重复一下映射的过程,同时这里会进行数据校验,其中包含文件大小和CRC校验

  • CRC:通过验证则将文件数据读入m_crcDigest进行记录并将文件读入到m_dic,不通则回调异常onMMKVCRCCheckFail清空缓存文件数据,此次写入作废,有效防止恶意攻击

之后如果有回写标示(是在后续encode确认空间够用时标记)进行回写数据去重⚠️。

到这我们就基本完成了构建工作,并且有了m_dic,这块内存缓存后期就能被我们所用进行快速的查询。

3、写入:encode

调用链路(以string为例):native-bridge.cpp:encodeString:set:MMKV_IO.cpp:setDataForKey

除了开始有一个简单的判断值是否有效外,最终就是setDataForKey的舞台了

这里我们重点关注下不实用密钥的情况,与有密钥对大体一致,只是有密钥对会有一些加密数据判断处理

分两种情况:在缓存中找到 or 未找到。最后更散列

在缓存中找到了

首先计算出key与value经过ProtocolBuffer(ProtocolBuffer编码)编码完了的长度并进行空间验证,如果空间不足,进行扩容

扩容

  1. 前提:剩余可使用大小现在小于要写入的新值的大小 or m_dic是空的
  2. 会提前准备一些条件进行校验,如:现在所需要的空间、每个键值的平均大小、未来可能需要的大小
  3. 没有空间进行完全回写,请将其加倍;空间不足以供将来使用,请将其加倍以避免频繁完全回写
  4. 进行一次回写

最后的写入操作:doAppendDataWithKey

  1. 首先在确认一下可用大小:ensureMemorySize
  2. 通过m_output进行key、value写入到映射文件内存
  3. 步骤2完毕后还有有一些加密的判断操作以及CRC的更新

4、读取:decode

调用链路:native-bridge.cpp:decodeString  :MMKV.cpp:getString

读取相对来说很简单

  1. 从内存m_dic中获取到数据的MMBuffer
  2. 通过CodedInputData获得其中真正的value

5、其他:多进程、文件锁等

这里就不多说了,官方给了非常详细的解答:MMKV for Android 多进程设计与实现

参考文献:

存储性能优化 MMKV源码解析 - 简书前言 好久没有更新常用的第三方库了。让我们来聊聊MMKV这个常用的第三方库。MMKV这个库是做什么的呢?他本质上的定位和sp有点相似,经常用于持久化小数据的键值对。其速度可以...https://www.jianshu.com/p/c12290a9a3f7

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值