• SharePreference与MMKV对比


    本文来自刘兆贤的博客_CSDN博客-Java高级,Android旅行,Android基础领域博主 ,引用必须注明出处!

    先了解一种Android特有的存储机制,快写机制MMap。

    MMap:Memory Mapping,内存映射(升级IPC数据传输)。将一块物理内存(可随时读写的),通过文件操作符,同时映射到用户虚拟内存空间,和内核虚拟地址空间(struct vm_struct *area,属于逻辑地址,区别于物理地址,可通过转换器计算)的一段内存块(两者大小相同)。

    多线程:通过加锁(mutex_lock),保证每次只有一个进程被分配内存。

    实现原理:App进程负责写数据,由操作系统负责将内存写入文件。用于快速存储,解决日志写入慢的问题。

    代表性组件:MMKV,Logan

    MMKV存储目录:/data/user/0/applicationId/files/mmkv

    在我看来MMKV只有三个优势,

    1、增量更新,对比全量更新每次覆盖文件,速度更快,防止数据丢失;

    2、存储利用MMAP机制,效率高于文件IO操作;

    3、多应用进程共享时,加了进程锁,而SP不具备;

    4、如果非加一条就是使用protobuffer协议,数据量较xml更少。

    缺点,当存储空间达到一定量级后,需要重新整理(如重复数据删除,key排序等)。

    处理三个问题:指针增长、内存增长、内存整理。通过指针增长,其他进程获得数据变化情况。

    SharePreference的实现类SharePreferenceImpl,使用CountDownLatch做同步,commit是先写入内存,同时同步写入磁盘,等待内存写入成功,就返回结果。而apply是异步写入磁盘。写内存和文件时都加锁。

    https://developer.android.google.cn/guide/topics/manifest/service-element

    经过LocalService和RemoteService实验,得知只要是当前APP开启的进程,数据均存储在data/data/applicationId/shared_prefs目录下。

    Note: This class does not support use across multiple processes.这句话的意思,应该是非同一APP的进程,不能共享数据。

    put/remove ,都是线程级同步操作。

    SharePreferenceImpl类中取数据方法:

    1. @Override
    2. @Nullable
    3. public String getString(String key, @Nullable String defValue) {
    4. synchronized (mLock) {
    5. awaitLoadedLocked();
    6. String v = (String)mMap.get(key);
    7. return v != null ? v : defValue;
    8. }
    9. }

    SharePreferenceImpl.EditorImpl

    1. @Override
    2. public Editor putString(String key, @Nullable String value) {
    3. synchronized (mEditorLock) {
    4. mModified.put(key, value);
    5. return this;
    6. }
    7. }

    https://developer.android.google.cn/reference/android/content/SharedPreferences

    MMKV对比SharePreference,前者使用ProtoBuffer协议、增量更新,后者使用XML协议、全量更新

    SharePreference使用优化:如果同一时间需要存储很多数据,可以最后再apply,避免多余的任务;常用和不常用的数据,分开保存,避免不必要的IO处理。

    MMKV:使用MMap做快速存储的组件,适用于需要日志写入系统。

    特点:立马生效,不存在异步的问题。

    写数据机制:key/value不断写在文件的后面(即使有相同的key),取值时从后往前查找,使用最新的值。默认映射内存为4kb,不足时扩充1倍。

    区别于SharePreference每次都要重写文件,多进程时有几率造成数据丢失,而MMKV通过MMap映射到内核空间,使用文件锁的方式,可以解决此问题。

    多进程时,需要解决数据同步的问题,采用文件互斥锁(同时只有一个线程拿到),而非pthead_mutex(Binder死亡通知方案-此通知不能自己处理,A、B进程互相注册对方死亡通知,A一直存在就有一个永久加锁的mutext,无法释放)。在健壮性的条件下,还要考虑锁升降级和递归加锁问题,文件锁不支持,但mutext缺点更明显。

    每个进程自己拥有一个写指针,mmap内存中也有一个写指针,对比两者偏移量,来做同步;使用单调递增的序列号,来判断内存是否已经重整(原指针之前的数据全部失效,需要重新加载);重整后内存不足,才需要申请新的内存。

    递归锁:属于状态锁,没有计数器,一次解锁全部解掉。

    锁升级:共享锁升级到互斥锁,读锁升级到写锁,如果多个进程都要升级,则会出现互相等待的死锁情况。

    因此,加写锁时,需要先加写锁,再释放读锁;解写锁时,同样要先加读锁,再解写锁。

    https://github.com/Tencent/MMKV/wiki/android_ipc

    使用:

    默认明文保存,可加密,可换密钥,可转换为明文。

    默认单进程模式,可设置多进程模式。

    默认存放在FileDir目录下,可更换目录。

    可备份和恢复。

    问题:

    某些设备找不到so库,可以用ReLinker(https://github.com/KeepSafe/ReLinker)来解决。

    https://github.com/Tencent/MMKV/wiki/android_advance_cn

    GitHub - Tencent/MMKV: An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, and POSIX.

  • 相关阅读:
    《C++》动态内存管理
    nodejs的tream(流)解析与模拟文件读写流源码实现
    二叉树进阶——手撕二叉搜索树
    智能化工厂数字孪生可视化展示更大限度优化质量流程
    【C++】动态多态与虚函数
    ThreeJS-3D教学八-OBJLoader模型加载+动画
    【记事本 】dom操作的类型
    深度学习入门(二十九)卷积神经网络——VGG
    数据通信——传输层TCP(TCP报文格式)
    认识Java中的类和对象(下)
  • 原文地址:https://blog.csdn.net/liuxian13183/article/details/126151957