当前位置: 首页 > 工具软件 > MMKV > 使用案例 >

SharePreference与MMKV对比

齐高寒
2023-12-01

本文来自刘兆贤的博客_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类中取数据方法:

    @Override
    @Nullable
    public String getString(String key, @Nullable String defValue) {
        synchronized (mLock) {
            awaitLoadedLocked();
            String v = (String)mMap.get(key);
            return v != null ? v : defValue;
        }
    }

SharePreferenceImpl.EditorImpl

     @Override
        public Editor putString(String key, @Nullable String value) {
            synchronized (mEditorLock) {
                mModified.put(key, value);
                return this;
            }
        }

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.

 类似资料: