MMKV 原理以及使用

介绍

MMKV是基于mmap内存映射的移动端通用key-value组件,底层序列化/反序列化使用protobuf实现,性能高,稳定性强。从2015年中至今,在iOS微信上使用已有近3年,近期移植到Android平台,移动端全平台通用,并全部在Github上开源。

MMKV 原理

内存准备:
  通过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由操作系统负责将内存回写到文件,不必担心 crash 导致数据丢失。
数据组织:
  数据序列化方面我们选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现。考虑到我们要提供的是通用 kv 组件,key 可以限定是 string 字符串类型,value 则多种多样(int/bool/double 等)。要做到通用的话,考虑将 value 通过 protobuf 协议序列化成统一的内存块(buffer),然后就可以将这些 KV 对象序列化到内存中。
写入优化: (重点关注这里!!!)
  标准 protobuf 不提供增量更新的能力,每次写入都必须全量写入。考虑到主要使用场景是频繁地进行写入更新,我们需要有增量更新的能力:将增量 kv 对象序列化后,直接 append 到内存末尾;这样同一个 key 会有新旧若干份数据,最新的数据在最后;那么只需在程序启动第一次打开 mmkv 时,不断用后读入的 value 替换之前的值,就可以保证数据是最新有效的。
空间增长: (还有这里!!!)
  使用 append 实现增量更新带来了一个新的问题,就是不断 append 的话,文件大小会增长得不可控。例如同一个 key 不断更新的话,是可能耗尽几百 M 甚至上 G 空间,而事实上整个 kv 文件就这一个 key,不到 1k 空间就存得下。这明显是不可取的。我们需要在性能和空间上做个折中:以内存 pagesize 为单位申请空间,在空间用尽之前都是 append 模式;当 append 到文件末尾时,进行文件重整、key 排重,尝试序列化保存排重结果;排重后空间还是不够用的话,将文件扩大一倍,直到空间足够。
数据有效性:
  考虑到文件系统、操作系统都有一定的不稳定性,我们另外增加了 crc 校验,对无效数据进行甄别。在 iOS 微信现网环境上,我们观察到有平均约 70万日次的数据校验不通过。

MMKV 使用

依赖:

implementation 'com.tencent:mmkv:1.0.19'

在Application里面初始化:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    String rootDir = MMKV.initialize(this);//就这么一句话就行
    System.out.println("mmkv root: " + rootDir);
}

支持的数据类型:

支持以下 Java 语言基础类型:
boolean、int、long、float、double、byte[],String、Set<String>,任何实现了Parcelable的类型,对象存储方式是,转化成json串,通过字符串存储,使用的时候在取出来反序列化.

增:

MMKV kv = MMKV.defaultMMKV();

kv.encode("bool", true);
System.out.println("bool: " + kv.decodeBool("bool"));

kv.encode("int", Integer.MIN_VALUE);
System.out.println("int: " + kv.decodeInt("int"));

kv.encode("long", Long.MAX_VALUE);
System.out.println("long: " + kv.decodeLong("long"));

kv.encode("float", -3.14f);
System.out.println("float: " + kv.decodeFloat("float"));

kv.encode("double", Double.MIN_VALUE);
System.out.println("double: " + kv.decodeDouble("double"));

kv.encode("string", "Hello from mmkv");
System.out.println("string: " + kv.decodeString("string"));

byte[] bytes = {'m', 'm', 'k', 'v'};
kv.encode("bytes", bytes);
System.out.println("bytes: " + new String(kv.decodeBytes("bytes")));

注意:mmkv的写入逻辑是:当我们覆盖某个值的时候,它并不会立即删除前面的值,会保留,然后每个key,value有存储限制,当触发存储限制的时候,才会执行删除,这样即使我们频繁的覆盖,也不会引起太多的性能损耗

删:

MMKV kv = MMKV.defaultMMKV();

kv.removeValueForKey("bool");
System.out.println("bool: " + kv.decodeBool("bool"));
    
kv.removeValuesForKeys(new String[]{"int", "long"});
System.out.println("allKeys: " + Arrays.toString(kv.allKeys()));

改:

直接在存一遍就是.(执行增步骤)

查:

在增的步骤里面,已经打印可查的结果.

 kv.decodeBool("bool");
 kv.decodeInt("int");
.....

如果不同业务需要区别存储,也可以单独创建自己的实例:

MMKV* mmkv = MMKV.mmkvWithID("MyID");
mmkv.encode("bool", true);

SharedPreferences 迁移


    MMKV preferences = MMKV.mmkvWithID("myData");
    // 迁移旧数据
    {
        SharedPreferences old_man = getSharedPreferences("myData", MODE_PRIVATE);
        preferences.importFromSharedPreferences(old_man);
        old_man.edit().clear().commit();
    }
    // 跟以前用法一样
    SharedPreferences.Editor editor = preferences.edit();
    editor.putBoolean("bool", true);
    editor.putInt("int", Integer.MIN_VALUE);
    editor.putLong("long", Long.MAX_VALUE);
    editor.putFloat("float", -3.14f);
    editor.putString("string", "hello, imported");
    HashSet<String> set = new HashSet<String>();
    set.add("W"); set.add("e"); set.add("C"); set.add("h"); set.add("a"); set.add("t");
    editor.putStringSet("string-set", set);
    // 无需调用 commit()
    //editor.commit();

以上内容来自官方github:https://github.com/Tencent/MMKV/wiki/android_setup_cn



作者:鹅鹅鹅曲项向天歌呀
链接:https://www.jianshu.com/p/fe8a827ceffe
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

### MMKV Android 实现原理与源码分析 #### 1. MMKV 的核心概念 MMKV 是腾讯开源的一款基于 mmap 文件映射技术的移动端通用 key-value 组件,支持多进程访问和加密存储功能。其设计目标是为了替代传统的 SharedPreferences 和其他类似的轻量级存储方案[^2]。 #### 2. 数据存储机制 MMKV 使用内存映射文件(memory-mapped file, mmap)来实现高效的读写操作。通过 mmap 技术,文件被映射到内存空间中,应用程序可以直接对这块内存进行修改,而无需手动管理磁盘 I/O 操作。这种机制显著提高了性能并减少了复杂度[^1]。 具体来说,MMKV 将所有的键值对序列化为二进制格式,并将其保存在一个单独的文件中。为了提高效率,它采用了紧凑的数据结构以及自定义的编码解码逻辑,从而减少不必要的开销[^3]。 #### 3. 多进程同步处理 在 Android 平台上,多个进程可能同时访问同一个共享资源。为此,MMKV 利用了 Linux 提供的 futex 来完成跨进程锁的功能,确保不同进程间能够安全地更新同一份数据而不发生冲突。 此外,当某个进程崩溃或者异常退出时,MMKV 能够自动检测这种情况并通过重新加载最新的持久化状态恢复一致性。 #### 4. 加密保护措施 对于敏感信息的安全性考虑,MMKV 支持 AES-256-GCM 算法来进行端到端的内容加密封装。开发者可以选择开启此选项以保障用户的隐私不受侵犯。 #### 5. 性能优化策略 除了采用 mmap 这一高效手段外,还有一些额外的技术细节帮助提升了整体表现: - **分片存储**:建议按照业务需求划分不同的子模块分别创建独立实例,这样既能降低单个文件大小又能缓解频繁 GC 对应用的影响; - **及时清理无用对象**:提供 `trim` API 让用户可以在适当时候主动回收未使用的缓存区域; - **关闭不必要连接**:如果某些场景下确实不再需要用到特定 KV 表,则应尽早调用对应的销毁方法释放关联资源。 ```java // 示例代码展示如何初始化及基本增删改查操作 import com.tencent.mmkv.MMKV; public class Example { public static void main(String[] args) { MMKV kv = MMKV.defaultMMKV(); // 插入字符串类型 value kv.encode("key", "value"); // 获取指定 key 下面存储的 string 类型 data String result = kv.decodeString("key", ""); System.out.println(result); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值