内容简介:在使用MMKV框架前,需调用以下方法进行初始化这里的Java层主要是获取到保存文件的路径,传入Native层,这里默认的路径是APP的内部存储目录下的mmkv路径,这里不支持修改,如需修改,需将源码clone下来手动修改编译了。到了Native层,通过Java_com_tencent_mmkv_MMKV_initialize方法跳转到MMKV::initializeMMKV方法里,启动了一个线程做初始化,然后检查内部路径是否存在,不存在则创建之。
在使用MMKV框架前,需调用以下方法进行初始化
MMKV.initialize(context); 复制代码
这里的 Java 层主要是获取到保存文件的路径,传入Native层,这里默认的路径是APP的内部存储目录下的mmkv路径,这里不支持修改,如需修改,需将源码clone下来手动修改编译了。
public static String initialize(Context context) { String rootDir = context.getFilesDir().getAbsolutePath() + "/mmkv"; initialize(rootDir); return rootDir; } 复制代码
到了Native层,通过Java_com_tencent_mmkv_MMKV_initialize方法跳转到MMKV::initializeMMKV方法里,启动了一个线程做初始化,然后检查内部路径是否存在,不存在则创建之。
void MMKV::initializeMMKV(const std::string &rootDir) { static pthread_once_t once_control = PTHREAD_ONCE_INIT; pthread_once(&once_control, initialize); g_rootDir = rootDir; char *path = strdup(g_rootDir.c_str()); mkPath(path); free(path); MMKVInfo("root dir: %s", g_rootDir.c_str()); } 复制代码
获取MMKV对象
获取MMKV对象的方法有以下几个,最傻瓜式的defaultMMKV到最复杂的mmkvWithAshmemID方法,按需调用。
public MMKV defaultMMKV(); public MMKV defaultMMKV(int mode, String cryptKey); public MMKV mmkvWithID(String mmapID); public MMKV mmkvWithID(String mmapID, int mode); public MMKV mmkvWithID(String mmapID, int mode, String cryptKey); @Nullable public MMKV mmkvWithAshmemID(Context context, String mmapID, int size, int mode, String cryptKey); 复制代码
上面的方法,基本都会来到getMMKVWithID方法,然后跳转到MMKV::mmkvWithID里
MMKV *MMKV::mmkvWithID(const std::string &mmapID, int size, MMKVMode mode, string *cryptKey) { if (mmapID.empty()) { return nullptr; } SCOPEDLOCK(g_instanceLock); auto itr = g_instanceDic->find(mmapID); if (itr != g_instanceDic->end()) { MMKV *kv = itr->second; return kv; } auto kv = new MMKV(mmapID, size, mode, cryptKey); (*g_instanceDic)[mmapID] = kv; return kv; } 复制代码
g_instanceDic是Map对象,先是根据mmapID在g_instanceDic进行查找,有直接返回,没就新建一个MMKV对象,然后再添加到g_instanceDic里。
MMKV::MMKV(const std::string &mmapID, int size, MMKVMode mode, string *cryptKey) : m_mmapID(mmapID) , m_path(mappedKVPathWithID(m_mmapID, mode)) , m_crcPath(crcPathWithID(m_mmapID, mode)) , m_metaFile(m_crcPath, DEFAULT_MMAP_SIZE, (mode & MMKV_ASHMEM) ? MMAP_ASHMEM : MMAP_FILE) , m_crypter(nullptr) , m_fileLock(m_metaFile.getFd()) , m_sharedProcessLock(&m_fileLock, SharedLockType) , m_exclusiveProcessLock(&m_fileLock, ExclusiveLockType) , m_isInterProcess((mode & MMKV_MULTI_PROCESS) != 0) , m_isAshmem((mode & MMKV_ASHMEM) != 0) { m_fd = -1; m_ptr = nullptr; m_size = 0; m_actualSize = 0; m_output = nullptr; if (m_isAshmem) { m_ashmemFile = new MmapedFile(m_mmapID, static_cast<size_t>(size), MMAP_ASHMEM); m_fd = m_ashmemFile->getFd(); } else { m_ashmemFile = nullptr; } if (cryptKey && cryptKey->length() > 0) { m_crypter = new AESCrypt((const unsigned char *) cryptKey->data(), cryptKey->length()); } m_needLoadFromFile = true; m_crcDigest = 0; m_sharedProcessLock.m_enable = m_isInterProcess; m_exclusiveProcessLock.m_enable = m_isInterProcess; // sensitive zone { SCOPEDLOCK(m_sharedProcessLock); loadFromFile(); } } 复制代码
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类会保存住该地址,用于接下来的读写操作。
public static MMKV mmkvWithID(String mmapID, int mode, String cryptKey) { long handle = getMMKVWithID(mmapID, mode, cryptKey); return new MMKV(handle); } 复制代码
写数据
以写入String对象为例,看看写入步骤
public boolean encode(String key, String value) { return encodeString(nativeHandle, key, value); } 复制代码
来到MMKV::setStringForKey方法
bool MMKV::setStringForKey(const std::string &value, const std::string &key) { if (key.empty()) { return false; } auto data = MiniPBCoder::encodeDataWithObject(value); return setDataForKey(std::move(data), key); } 复制代码
MiniPBCoder::encodeDataWithObject方法将value构造出一个Protobuf数据对象(本章不对此详细分析),然后将构造出来的数据对象通过std::move方法传到setDataForKey里
bool MMKV::setDataForKey(MMBuffer &&data, const std::string &key) { if (data.length() == 0 || key.empty()) { return false; } SCOPEDLOCK(m_lock); SCOPEDLOCK(m_exclusiveProcessLock); checkLoadData(); // m_dic[key] = std::move(data); auto itr = m_dic.find(key); if (itr == m_dic.end()) { itr = m_dic.emplace(key, std::move(data)).first; } else { itr->second = std::move(data); } return appendDataWithKey(itr->second, key); } 复制代码
- checkLoadData()用来检查文件有效性(本章不对此详细分析)
- m_dic是一个Map对象,在这里判断是否已经存在该Key,有就替换,没就添加
- appendDataWithKey()是将该对象添加到内存里(本章不对此详细分析)
读数据
public String decodeString(String key, String defaultValue) { return decodeString(nativeHandle, key, defaultValue); } 复制代码
来到MMKV::getDataForKey方法
const MMBuffer &MMKV::getDataForKey(const std::string &key) { SCOPEDLOCK(m_lock); checkLoadData(); auto itr = m_dic.find(key); if (itr != m_dic.end()) { return itr->second; } static MMBuffer nan(0); return nan; } 复制代码
通过key在m_dic对象里进行查找,如果查找到,就返回,没则返回一个0长度的对象
到此,整体流程讲完了,还有很多原理性的东西,在后面的章节再讲,其中mmap映射的操作,还有protobuf的读写原理等。 如果本文对您有用的话,记得点一个赞哦!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- kubelet 分析源码:启动流程
- 【zookeeper源码】启动流程详解
- View绘制流程源码分析
- gorm查询流程源码分析
- ReactNative源码解析-启动流程
- Android 系统源码-1:Android 系统启动流程源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。