MMKV 几见

引言

最近微信开源了 mmkv,之前曾经深为 android 跨进程数据共享和通信所困惑,用 contextprovider 里面 sharedpreference,也曾经考虑过用文件读写来实现,可是 Java 端对文件读写跨进程操作实在是没有很大可操作余地,ndk写的话又太耗时而且无法保障测试性能等等问题。现在开源的 mmkv 正好弥补来这一块空缺,而且结果微信检验,在性能和安全方面感觉还是比较靠谱的。

详解

跨进程数据共享主要有以下问题:

  1. 多进程数据如何保持数据一致性即写更新,读的都是最新的
  2. 如何保证稳定性和高效性,降低性能消耗

mmkv 最初的设计并不是为了考虑多进程情况。主要是提高了 key-value 存储的性能。

  1. 使用 protobuf 二进制来存储数据。作为高效数据压缩编码方式,无疑提高了写入和读取性能
  2. 增量更新。通过将修改数据写在后面,等待内存满了之后触发重整进行整理。提高了修改操作的性能,不需要再去查询旧数据进行修改。当然在不断触发内存重整的情况下会大大损耗性能(回到),但一般情况下这明显是低概率事件.且存储限制会指数增长。
  3. mmap 文件映射内存,省去一次拷贝的时机。

而之后考虑 android 多进程的情况,针对多进程需要考虑情况:

  1. 指示器。拿文件前面几个字节作为当前写的位置。多进程模式下,每个进程读写时候都要检查一下当前和内存是否一致。不一致则需要读取新写的。
  2. 锁。使用了文件读写锁,在外部做了封装,可以更好支持。
  3. 增加了 Ashmem 的支持。

使用

  1. 使用简单,最好直接使用 static 的依赖,因为普通的依赖会添加 libc++_shared.so ,会导致包比 static 大2倍以上
    1
    implementation 'com.tencent:mmkv-static:1.0.19'
  2. 性能测试,多进程和单进程性能相差很小。1000 次写稳定在几十毫秒,在新机器上会达到20、30毫秒内。1000 次读能稳定在10毫秒左右。偶尔可能会有波动。总体看性能比读写 file 高10倍以上,比 sharedference 写高百倍(因为 sharedference 就算使用 apply,在最后未完成也要补回来),读因为 sharedference 是内存操作所以相差不大。
  3. 因为 mmkv 在 native 层做了较多缓存,所以在使用是可以不需要考虑创建性能单例等等问题

注意事项

  1. 使用 file 没有特别注意的地方,但是要注意自己不要每次都添加很大的数据,很频繁触发内存重整,效率会很低。

  2. 使用 Ashmem 的话,有很多注意地方。可以的话能不用就不用,使用 file + 逻辑来代替

    1. 内部实现实际使用了 MMKVContentProvider 来传递文件描述符
    2. 在 X86 某些机型上很容易 anr
    3. 如果 MMKVContentProvider 所在进程挂了重新启动,会导致 ashmem 生成新的,和其它还存在进程不一致。
  3. 因为 mmkv 无法保障原子性操作。类似乐观锁的需要自己实现

  4. mmkv 采用 mmap,实际上 binder 内部实现也是使用 mmap。所以不需要过多担心内部稳定性