数据持久化之轻量级Kv持久化(二)
阿里P7Android高级架构进阶视频免费学习请点击:https://space.bilibili.com/474380680
本篇文章将继续从以下两个内容来介绍轻量级Kv持久化:
- [SharedPreferences详解与原理分析]
- [ 微信MMKV源码分析]
一、SharedPreferences详解与原理分析
SharedPreferences作为Android存储数据方式之一,主要特点是:
只支持Java基本数据类型,不支持自定义数据类型;
应用内数据共享;
使用简单.
使用方法
1、存数据
或者下面的写法也可以
切记不要写成下面的形式,会导致数据无法存储
为什么这种方式无法存储,因为sp.edit()每次都会返回一个新的Editor对象,Editor的实现类EditorImpl里面会有一个缓存的Map,最后commit的时候先将缓存里面的Map写入内存中的Map,然后将内存中的Map写进XML文件中。使用上面的方式commit,由于sp.edit()又重新返回了一个新的Editor对象,缓存中的Map是空的,所以导致数据无法被存储。
2、取数据
getSharedPreferences的具体实现是在frameworks/base/core/java/android/app/ContextImpl.java,代码如下:
SharedPreferencesImpl是SharedPreferences接口的具体实现类,一个name对应一个SharedPreferencesImpl,一个应用程序中根据name的不同会有多个SharedPreferencesImpl。
SharedPreferencesImpl的具体实现是在frameworks/base/core/java/android/app/SharedPreferencesImpl.java,我们可以通过getSharedPreferences获得SharedPreferences的实例,当我们调用sp.getString等get方法取数据时,实际上是直接从内存中的Map里面去取,get方法传入的第一个参数正好是Map的key,第二个参数是当Map中没有这个key对应值的时候,返回的默认值。
二、微信MMKV源码分析
2.1整体流程
初始化
在使用MMKV框架前,需调用以下方法进行初始化
这里的Java层主要是获取到保存文件的路径,传入Native层,这里默认的路径是APP的内部存储目录下的mmkv路径,这里不支持修改,如需修改,需将源码clone下来手动修改编译了。
到了Native层,通过Java_com_tencent_mmkv_MMKV_initialize方法跳转到MMKV::initializeMMKV方法里,启动了一个线程做初始化,然后检查内部路径是否存在,不存在则创建之。
获取MMKV对象
获取MMKV对象的方法有以下几个,最傻瓜式的defaultMMKV到最复杂的mmkvWithAshmemID方法,按需调用。
上面的方法,基本都会来到getMMKVWithID方法,然后跳转到MMKV::mmkvWithID里
g_instanceDic是Map对象,先是根据mmapID在g_instanceDic进行查找,有直接返回,没就新建一个MMKV对象,然后再添加到g_instanceDic里。
MMKV的构造函数里,做了一系列参数的构造,分别有:
- m_mmapID:文件名
- m_path:存放路径
- m_crcPath:校验文件存放路径
- m_metaFile:内存映射的管理对象
- m_crypter:AES加密密钥
- m_lock:线程锁
- m_fileLock:文件锁
- m_sharedProcessLock:映射文件到内存的锁
- m_exclusiveProcessLock:在内存读写数据时的锁
- m_isInterProcess:是否主进程
- m_isAshmem:是否匿名内存
- m_ptr:文件映射到内存后的地址
- m_size:文件大小
- m_actualSize:内存大小,这个会因为写数据动态变化
- m_output:Protobuf对象,用于写文件,效率之所高,这里也很关键
- m_ashmemFile:匿名内存的文件对象
- m_needLoadFromFile:一个标识对象,用于是否加载过文件,加载过就将它置为false
- m_crcDigest:数据校验
MMKV对象构造完毕后,会将该对象的指针地址返回给Java层,Java层的MMKV类会保存住该地址,用于接下来的读写操作。
写数据
以写入String对象为例,看看写入步骤
来到MMKV::setStringForKey方法
MiniPBCoder::encodeDataWithObject方法将value构造出一个Protobuf数据对象(本章不对此详细分析),然后将构造出来的数据对象通过std::move方法传到setDataForKey里
- checkLoadData()用来检查文件有效性(本章不对此详细分析)
- m_dic是一个Map对象,在这里判断是否已经存在该Key,有就替换,没就添加
- appendDataWithKey()是将该对象添加到内存里(本章不对此详细分析)
读数据
来到MMKV::getDataForKey方法
通过key在m_dic对象里进行查找,如果查找到,就返回,没则返回一个0长度的对象。
2.2 MMAP映射
加载文件
参数解释
mmap函数是MMKV的干货之一了,如果没有这个函数的存在,或许就没有今天的MMKV了,下面说下这个函数的参数和使用方法。
mmap (一种内存映射文件的方法)
mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。mmap在用户空间映射调用系统中作用很大。
头文件 <sys/mman.h>
函数原型
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
start:映射区的开始地址。设置null即可。
length:映射区的长度。传入文件对齐后的大小m_size。
prot:期望的内存保护标志,不能与文件的打开模式冲突。设置可读可写。
flags:指定映射对象的类型,映射选项和映射页是否可以共享。设置MAP_SHARED表示可进程共享,MMKV之所以可以实现跨进程使用,这里是关键。
fd:有效的文件描述词。用上面所打开的m_fd。
off_toffset:被映射对象内容的起点。从头开始,比较好理解。
内存重组
在跨进程读写的时候,进程A修改了一个数据,进程B去读的时候,就会校验内存的数据和文件的数据,一旦不相同,就说明有了跨进程的操作,这个时候就需要内存重组,清空原有数据,重新读最新的文件映射到内存中。
阿里P7Android高级架构进阶视频免费学习请点击:https://space.bilibili.com/474380680
参考https://juejin.im/post/5baf8ae8f265da0ae92a7df5
https://juejin.im/post/5bac285d5188255c7039ab80
https://blog.csdn.net/lyl278401555/article/details/50610790
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
· 分享一个我遇到过的“量子力学”级别的BUG。
· Linux系列:如何调试 malloc 的底层源码
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· 对象命名为何需要避免'-er'和'-or'后缀
· JDK 24 发布,新特性解读!
· C# 中比较实用的关键字,基础高频面试题!
· .NET 10 Preview 2 增强了 Blazor 和.NET MAUI
· SQL Server如何跟踪自动统计信息更新?