深入理解Android MMKV数据读取机制:从内存到磁盘的全解析
一、MMKV数据读取概述
1.1 读取流程简介
MMKV的数据读取流程主要包括以下几个关键步骤:
- 从内存缓存中快速查找数据
- 如果内存中不存在,则从文件映射中读取
- 对加密数据进行解密处理
- 反序列化数据并返回给调用者
1.2 核心组件
数据读取涉及的核心组件包括:
- 内存映射文件:通过mmap将文件映射到内存
- 内存缓存:存储最近访问的数据
- 数据解析器:解析不同类型的数据
- 加密模块:对加密数据进行解密
1.3 性能优势
MMKV的读取性能优势主要体现在:
- 内存映射避免了频繁的磁盘IO
- 高效的数据结构和算法
- 支持并发读取
- 数据缓存机制减少了重复解析
二、Java层读取API
2.1 基础读取方法
// MMKV.java
/**
* 读取布尔值
* @param key 键
* @param defaultValue 默认值
* @return 存储的布尔值,如果不存在则返回默认值
*/
public boolean getBoolean(String key, boolean defaultValue) {
// 检查是否为多进程模式
if (m_isInterProcess) {
// 多进程模式下,先检查文件是否有更新
checkLoadData();
}
// 调用本地方法读取布尔值
return nativeGetBool(m_nativeHandle, key, defaultValue);
}
/**
* 读取int值
* @param key 键
* @param defaultValue 默认值
* @return 存储的int值,如果不存在则返回默认值
*/
public int getInt(String key, int defaultValue) {
if (m_isInterProcess) {
checkLoadData();
}
// 调用本地方法读取int值
return nativeGetInt(m_nativeHandle, key, defaultValue);
}
// 其他类型的读取方法类似...
2.2 批量读取方法
// MMKV.java
/**
* 获取所有键值对
* @return 包含所有键值对的Map
*/
public Map<String, ?> getAll() {
if (m_isInterProcess) {
checkLoadData();
}
// 调用本地方法获取所有键值对
return nativeGetAll(m_nativeHandle);
}
/**
* 检查是否包含某个键
* @param key 键
* @return 如果包含该键则返回true,否则返回false
*/
public boolean containsKey(String key) {
if (m_isInterProcess) {
checkLoadData();
}
// 调用本地方法检查是否包含键
return nativeContainsKey(m_nativeHandle, key);
}
2.3 多进程模式下的读取
// MMKV.java
/**
* 检查并加载最新数据(多进程模式)
*/
private void checkLoadData() {
if (m_needLoadData) {
synchronized (this) {
if (m_needLoadData) {
// 调用本地方法重新加载数据
nativeReloadData(m_nativeHandle);
m_needLoadData = false;
}
}
}
}
三、JNI层数据读取
3.1 JNI方法映射
// Jni.cpp
static JNINativeMethod gMethods[] = {
// 映射Java层的nativeGetBool方法到C++层的jniGetBool方法
{"nativeGetBool", "(JLjava/lang/String;Z)Z", (void *) jniGetBool},
// 映射Java层的nativeGetInt方法到C++层的jniGetInt方法
{"nativeGetInt", "(JLjava/lang/String;I)I", (void *) jniGetInt},
// 映射Java层的nativeGetLong方法到C++层的jniGetLong方法
{"nativeGetLong", "(JLjava/lang/String;J)J", (void *) jniGetLong},
// 映射Java层的nativeGetFloat方法到C++层的jniGetFloat方法
{"nativeGetFloat", "(JLjava/lang/String;F)F", (void *) jniGetFloat},
// 映射Java层的nativeGetDouble方法到C++层的jniGetDouble方法
{"nativeGetDouble", "(JLjava/lang/String;D)D", (void *) jniGetDouble},
// 映射Java层的nativeGetString方法到C++层的jniGetString方法
{"nativeGetString", "(JLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void *) jniGetString},
// 映射Java层的nativeGetByteArray方法到C++层的jniGetByteArray方法
{"nativeGetByteArray", "(JLjava/lang/String;[B)[B", (void *) jniGetByteArray},
// 映射Java层的nativeGetAll方法到C++层的jniGetAll方法
{"nativeGetAll", "(J)Ljava/util/HashMap;", (void *) jniGetAll},
// 映射Java层的nativeContainsKey方法到C++层的jniContainsKey方法
{"nativeContainsKey", "(JLjava/lang/String;)Z", (void *) jniContainsKey},
// 映射Java层的nativeReloadData方法到C++层的jniReloadData方法
{"nativeReloadData", "(J)V", (void *) jniReloadData},
// 其他方法映射...
};
3.2 基本类型读取实现
// Jni.cpp
// 读取布尔值
static jboolean jniGetBool(JNIEnv *env, jobject, jlong handle, jstring jkey, jboolean jdef) {
// 将Java对象转换为C++对象
MMKV *mmkv = (MMKV *) handle;
if (!mmkv) {
return jdef;
}
// 将Java字符串转换为C++字符串
const char *key = env->GetStringUTFChars(jkey, nullptr);
if (!key) {
return jdef;
}
// 调用MMKV的getBool方法读取布尔值
bool result = mmkv->getBool(key, (bool) jdef);
// 释放Java字符串资源
env->ReleaseStringUTFChars(jkey, key);
return (jboolean) result;
}
// 读取int值
static jint jniGetInt(JNIEnv *env, jobject, jlong handle, jstring jkey, jint jdef) {
MMKV *mmkv = (MMKV *) handle;
if (!mmkv) {
return jdef;
}
const char *key = env->GetStringUTFChars(jkey, nullptr);
if (!key) {
return jdef;
}
// 调用MMKV的getInt32方法读取int值
int32_t result = mmkv->getInt32(key, (int32_t) jdef);
env->ReleaseStringUTFChars(jkey, key);
return (jint) result;
}
// 其他基本类型的读取方法实现类似...
3.3 复杂类型读取实现
// Jni.cpp
// 读取字符串
static jstring jniGetString(JNIEnv *env, jobject, jlong handle, jstring jkey, jstring jdef) {
MMKV *mmkv = (MMKV *) handle;
if (!mmkv) {
return jdef;
}
const char *key = env->GetStringUTFChars(jkey, nullptr);
if (!key) {
return jdef;
}
// 调用MMKV的getString方法读取字符串
string result = mmkv->getString(key, "");
env->ReleaseStringUTFChars(jkey, key);
if (result.empty()) {
return jdef;
}
// 将C++字符串转换为Java字符串
return env->NewStringUTF(result.c_str());
}
// 读取字节数组
static jbyteArray jniGetByteArray(JNIEnv *env, jobject, jlong handle, jstring jkey, jbyteArray jdef) {
MMKV *mmkv = (MMKV *) handle;
if (!mmkv) {
return jdef;
}
const char *key = env->GetStringUTFChars(jkey, nullptr);
if (!key) {
return jdef;
}
// 调用MMKV的getByteArray方法读取字节数组
MMBuffer result = mmkv->getByteArray(key, MMBuffer());
env->ReleaseStringUTFChars(jkey, key);
if (!result.length()) {
return jdef;
}
// 创建Java字节数组并复制数据
jbyteArray array = env->NewByteArray((jsize) result.length());
if (array) {
env->SetByteArrayRegion(array, 0, (jsize) result.length(), (const jbyte *) result.getPtr());
}
return array;
}
四、C++层数据读取核心
4.1 内存映射文件读取
// MMKV.cpp
// 从内存映射文件中读取数据
bool MMKV::getBool(const char *key, bool defaultValue) {
// 加共享锁,允许多线程并发读取
SCOPED_LOCK_SHARED;
// 检查是否需要从文件重新加载数据
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
// 查找键对应的数据
auto itr = m_dic.find(key);
if (itr != m_dic.end()) {
// 获取数据缓冲区
MMBuffer &buffer = itr->second;
if (buffer.length() >= Fixed32Size) {
// 读取数据类型
int32_t type = *(int32_t *) buffer.getPtr();
if (type == TypeBool) {
// 布尔值占4个字节,转换为bool类型
return *(int32_t *) (buffer.getPtr() + Fixed32Size) != 0;
}
}
}
// 如果未找到或类型不匹配,返回默认值
return defaultValue;
}
// 读取int32值
int32_t MMKV::getInt32(const char *key, int32_t defaultValue) {
SCOPED_LOCK_SHARED;
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
auto itr = m_dic.find(key);
if (itr != m_dic.end()) {
MMBuffer &buffer = itr->second;
if (buffer.length() >= Fixed32Size) {
int32_t type = *(int32_t *) buffer.getPtr();
if (type == TypeInt32) {
// 读取int32值
return *(int32_t *) (buffer.getPtr() + Fixed32Size);
}
}
}
return defaultValue;
}
// 其他基本类型的读取方法实现类似...
4.2 字符串读取
// MMKV.cpp
// 读取字符串
string MMKV::getString(const char *key, const string &defaultValue) {
SCOPED_LOCK_SHARED;
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
auto itr = m_dic.find(key);
if (itr != m_dic.end()) {
MMBuffer &buffer = itr->second;
if (buffer.length() > Fixed32Size) {
int32_t type = *(int32_t *) buffer.getPtr();
if (type == TypeString) {
// 字符串长度(不包含类型标识的4个字节)
size_t length = buffer.length() - Fixed32Size;
// 复制字符串内容
return string((const char *) buffer.getPtr() + Fixed32Size, length);
}
}
}
return defaultValue;
}
4.3 字节数组读取
// MMKV.cpp
// 读取字节数组
MMBuffer MMKV::getByteArray(const char *key, const MMBuffer &defaultValue) {
SCOPED_LOCK_SHARED;
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
auto itr = m_dic.find(key);
if (itr != m_dic.end()) {
MMBuffer &buffer = itr->second;
if (buffer.length() > Fixed32Size) {
int32_t type = *(int32_t *) buffer.getPtr();
if (type == TypeBytes) {
// 复制字节数组内容(不包含类型标识的4个字节)
size_t length = buffer.length() - Fixed32Size;
return MMBuffer(buffer.getPtr() + Fixed32Size, length, MMBufferNoCopy);
}
}
}
return defaultValue;
}
4.4 复杂对象读取
// MMKV.cpp
// 读取自定义对象(示例)
bool MMKV::getCustomObject(const char *key, CustomObject &object) {
SCOPED_LOCK_SHARED;
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
auto itr = m_dic.find(key);
if (itr != m_dic.end()) {
MMBuffer &buffer = itr->second;
if (buffer.length() > Fixed32Size) {
int32_t type = *(int32_t *) buffer.getPtr();
if (type == TypeCustomObject) {
// 跳过类型标识
const uint8_t *ptr = buffer.getPtr() + Fixed32Size;
size_t remaining = buffer.length() - Fixed32Size;
// 从缓冲区解析对象
CodedInputData input(ptr, remaining);
// 读取对象的各个字段
int32_t field1 = 0;
string field2;
vector<int32_t> field3;
// 读取字段1
if (!input.readInt32(field1)) {
return false;
}
// 读取字段2
if (!input.readString(field2)) {
return false;
}
// 读取字段3
size_t field3Size = 0;
if (!input.readSize(field3Size)) {
return false;
}
field3.resize(field3Size);
for (size_t i = 0; i < field3Size; i++) {
if (!input.readInt32(field3[i])) {
return false;
}
}
// 填充对象
object.setField1(field1);
object.setField2(field2);
object.setField3(field3);
return true;
}
}
}
return false;
}
五、数据解析与反序列化
5.1 数据格式概述
MMKV使用自定义的二进制格式存储数据,每个数据项包含:
- 4字节类型标识
- 数据内容
5.2 CodedInputData类
// CodedInputData.cpp
// 从缓冲区读取固定长度的int32值
bool CodedInputData::readInt32(int32_t &value) {
if (m_position + Fixed32Size > m_size) {
return false;
}
// 从当前位置读取int32值
value = *(int32_t *) (m_ptr + m_position);
m_position += Fixed32Size;
return true;
}
// 从缓冲区读取字符串
bool CodedInputData::readString(string &value) {
size_t size = 0;
// 先读取字符串长度
if (!readSize(size)) {
return false;
}
if (m_position + size > m_size) {
return false;
}
// 复制字符串内容
value.assign((const char *) (m_ptr + m_position), size);
m_position += size;
return true;
}
// 从缓冲区读取变长大小值
bool CodedInputData::readSize(size_t &value) {
if (m_position >= m_size) {
return false;
}
// 读取一个字节
uint8_t byte = m_ptr[m_position++];
// 检查是否是单字节大小
if (!(byte & 0x80)) {
value = byte;
return true;
}
// 多字节大小值
uint64_t result = byte & 0x7F;
int shift = 7;
while (m_position < m_size) {
byte = m_ptr[m_position++];
result |= (uint64_t) (byte & 0x7F) << shift;
shift += 7;
if (!(byte & 0x80)) {
value = (size_t) result;
return true;
}
}
// 数据不完整
m_position = m_size;
return false;
}
5.3 数据类型解析
// MMKV.cpp
// 从内存映射文件加载所有数据
bool MMKV::loadFromFile() {
SCOPED_LOCK_EXCLUSIVE;
// 检查文件是否存在
if (!fileExists(m_path)) {
// 文件不存在,创建空的内存映射
m_actualSize = 0;
m_dirty = true;
return true;
}
// 读取文件内容
int fd = open(m_path.c_str(), O_RDONLY, 0);
if (fd < 0) {
MMKVError("fail to open file %s: %s", m_path.c_str(), strerror(errno));
return false;
}
// 获取文件大小
struct stat st = {0};
if (fstat(fd, &st) != 0) {
close(fd);
MMKVError("fail to fstat file %s: %s", m_path.c_str(), strerror(errno));
return false;
}
size_t fileSize = (size_t) st.st_size;
if (fileSize < Fixed32Size) {
// 文件太小,不可能包含有效数据
close(fd);
return false;
}
// 映射文件到内存
void *ptr = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0);
if (ptr == MAP_FAILED) {
close(fd);
MMKVError("fail to mmap file %s: %s", m_path.c_str(), strerror(errno));
return false;
}
// 读取头部信息
uint32_t header = *(uint32_t *) ptr;
m_actualSize = header & 0x0FFFFFFF;
if (m_actualSize + Fixed32Size > fileSize) {
// 实际数据大小超过文件大小,数据损坏
munmap(ptr, fileSize);
close(fd);
return false;
}
// 解析数据
bool loaded = parseData((const uint8_t *) ptr + Fixed32Size, m_actualSize);
// 解除内存映射
munmap(ptr, fileSize);
close(fd);
return loaded;
}
// 解析数据内容
bool MMKV::parseData(const uint8_t *ptr, size_t length) {
// 清空现有数据
m_dic.clear();
// 创建输入数据流
CodedInputData input(ptr, length);
// 解析所有键值对
while (input.available()) {
// 读取键
string key;
if (!input.readString(key)) {
return false;
}
// 读取值类型
int32_t type = 0;
if (!input.readInt32(type)) {
return false;
}
// 计算值的长度(剩余数据长度)
size_t valueLength = input.available();
// 根据类型解析值
switch (type) {
case TypeBool:
case TypeInt32:
// 基本类型,长度为4字节
if (valueLength < Fixed32Size) {
return false;
}
valueLength = Fixed32Size;
break;
case TypeInt64:
case TypeFloat:
case TypeDouble:
// 64位类型,长度为8字节
if (valueLength < Fixed64Size) {
return false;
}
valueLength = Fixed64Size;
break;
case TypeString:
case TypeBytes:
// 字符串和字节数组,长度由数据决定
size_t contentSize = 0;
if (!CodedInputData::readRawVarint32(ptr + input.position(), contentSize)) {
return false;
}
valueLength = contentSize + CodedInputData::computeSizeOfSize(contentSize);
if (valueLength > input.available()) {
return false;
}
break;
default:
// 未知类型,跳过
MMKVWarning("unknown value type: %d", type);
return false;
}
// 创建值缓冲区
MMBuffer value(input.position(), valueLength, MMBufferNoCopy);
// 将键值对添加到字典
m_dic[key] = std::move(value);
// 移动到下一个值
input.forward(valueLength);
}
return true;
}
六、加密数据读取
6.1 加密机制概述
MMKV支持数据加密,加密后的数据在读取时需要先解密。
6.2 解密流程
// MMKV.cpp
// 读取加密的布尔值
bool MMKV::getEncryptedBool(const char *key, bool defaultValue) {
SCOPED_LOCK_SHARED;
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
auto itr = m_dic.find(key);
if (itr != m_dic.end()) {
MMBuffer &buffer = itr->second;
if (buffer.length() > Fixed32Size) {
int32_t type = *(int32_t *) buffer.getPtr();
if (type == TypeEncryptedBool) {
// 创建解密后的缓冲区
MMBuffer decrypted(buffer.length() - Fixed32Size);
// 解密数据
if (m_crypter) {
m_crypter->decrypt((unsigned char *) decrypted.getPtr(),
(unsigned char *) buffer.getPtr() + Fixed32Size,
decrypted.length());
}
// 解析解密后的数据
if (decrypted.length() >= Fixed32Size) {
return *(int32_t *) decrypted.getPtr() != 0;
}
}
}
}
return defaultValue;
}
// 读取加密的字符串
string MMKV::getEncryptedString(const char *key, const string &defaultValue) {
SCOPED_LOCK_SHARED;
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
auto itr = m_dic.find(key);
if (itr != m_dic.end()) {
MMBuffer &buffer = itr->second;
if (buffer.length() > Fixed32Size) {
int32_t type = *(int32_t *) buffer.getPtr();
if (type == TypeEncryptedString) {
// 创建解密后的缓冲区
MMBuffer decrypted(buffer.length() - Fixed32Size);
// 解密数据
if (m_crypter) {
m_crypter->decrypt((unsigned char *) decrypted.getPtr(),
(unsigned char *) buffer.getPtr() + Fixed32Size,
decrypted.length());
}
// 解析解密后的数据
CodedInputData input((const uint8_t *) decrypted.getPtr(), decrypted.length());
string result;
if (input.readString(result)) {
return result;
}
}
}
}
return defaultValue;
}
七、多进程数据读取
7.1 多进程同步机制
// MMKV.cpp
// 多进程模式下检查并加载最新数据
bool MMKV::checkLoadDataIfNeeded() {
// 加进程间共享锁
SCOPED_LOCK_SHARED_PROCESS;
// 检查文件修改时间
struct stat st = {0};
if (stat(m_path.c_str(), &st) != 0) {
return false;
}
// 比较当前文件修改时间与上次记录的时间
if (st.st_mtime != m_lastModifiedTime || st.st_size != m_fileLength) {
// 文件已修改,需要重新加载
m_needLoadFromFile = true;
m_lastModifiedTime = st.st_mtime;
m_fileLength = st.st_size;
return true;
}
return false;
}
// 多进程模式下读取布尔值
bool MMKV::getBoolMultiProcess(const char *key, bool defaultValue) {
// 先检查是否需要加载最新数据
if (checkLoadDataIfNeeded()) {
// 加进程间排他锁,确保数据一致性
SCOPED_LOCK_EXCLUSIVE_PROCESS;
// 再次检查是否需要加载
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
}
// 加共享锁,允许多线程并发读取
SCOPED_LOCK_SHARED;
// 从内存缓存读取数据
auto itr = m_dic.find(key);
if (itr != m_dic.end()) {
MMBuffer &buffer = itr->second;
if (buffer.length() >= Fixed32Size) {
int32_t type = *(int32_t *) buffer.getPtr();
if (type == TypeBool) {
return *(int32_t *) (buffer.getPtr() + Fixed32Size) != 0;
}
}
}
return defaultValue;
}
7.2 文件锁机制
// FileLock.cpp
// 获取共享锁(读锁)
bool FileLock::lockShared() {
struct flock lock = {};
lock.l_type = F_RDLCK; // 共享锁
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0; // 锁定整个文件
// 调用fcntl系统调用获取锁
return (fcntl(m_fd, F_SETLKW, &lock) == 0);
}
// 获取排他锁(写锁)
bool FileLock::lockExclusive() {
struct flock lock = {};
lock.l_type = F_WRLCK; // 排他锁
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0; // 锁定整个文件
// 调用fcntl系统调用获取锁
return (fcntl(m_fd, F_SETLKW, &lock) == 0);
}
// 释放锁
bool FileLock::unlock() {
struct flock lock = {};
lock.l_type = F_UNLCK; // 解锁
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0; // 解锁整个文件
// 调用fcntl系统调用释放锁
return (fcntl(m_fd, F_SETLK, &lock) == 0);
}
八、性能优化与缓存机制
8.1 内存缓存策略
// MMKV.cpp
// 获取所有键值对
unordered_map<string, MMBuffer> MMKV::getAll() {
SCOPED_LOCK_SHARED;
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
// 返回内存缓存的副本
return m_dic;
}
// 检查是否包含某个键
bool MMKV::containsKey(const char *key) {
SCOPED_LOCK_SHARED;
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
// 检查内存缓存
return m_dic.find(key) != m_dic.end();
}
8.2 最近最少使用(LRU)缓存
// LRUCache.h
template<typename K, typename V>
class LRUCache {
private:
struct CacheEntry {
K key;
V value;
CacheEntry(const K &k, const V &v) : key(k), value(v) {}
};
// 双向链表存储缓存项
list<CacheEntry> m_cacheList;
// 哈希表存储键到链表迭代器的映射
unordered_map<K, typename list<CacheEntry>::iterator> m_cacheMap;
// 缓存容量
size_t m_capacity;
public:
explicit LRUCache(size_t capacity) : m_capacity(capacity) {}
// 获取缓存项
bool get(const K &key, V &value) {
auto itr = m_cacheMap.find(key);
if (itr != m_cacheMap.end()) {
// 找到缓存项,移到链表头部(最近使用)
auto listItr = itr->second;
CacheEntry entry = *listItr;
// 从链表中删除
m_cacheList.erase(listItr);
// 插入到链表头部
m_cacheList.push_front(entry);
// 更新映射
m_cacheMap[key] = m_cacheList.begin();
// 返回值
value = entry.value;
return true;
}
return false;
}
// 添加缓存项
void put(const K &key, const V &value) {
auto itr = m_cacheMap.find(key);
if (itr != m_cacheMap.end()) {
// 已存在,删除旧项
m_cacheList.erase(itr->second);
m_cacheMap.erase(itr);
}
// 检查是否达到容量限制
if (m_cacheMap.size() >= m_capacity) {
// 移除最旧的项
auto lastEntry = m_cacheList.back();
m_cacheMap.erase(lastEntry.key);
m_cacheList.pop_back();
}
// 添加新项到链表头部
m_cacheList.push_front(CacheEntry(key, value));
m_cacheMap[key] = m_cacheList.begin();
}
// 清空缓存
void clear() {
m_cacheList.clear();
m_cacheMap.clear();
}
};
// MMKV.cpp
// 使用LRU缓存优化读取性能
class MMKV {
private:
// LRU缓存,用于优化读取性能
LRUCache<string, MMBuffer> m_lruCache;
// ...其他成员变量...
public:
// 构造函数
MMKV(const string &path, MMKVMode mode, const string &cryptKey)
: m_path(path)
, m_mode(mode)
, m_lruCache(100) // 缓存100个最近使用的项
// ...其他初始化...
{
// ...构造函数其他代码...
}
// 优化的读取方法
MMBuffer getOptimized(const char *key) {
SCOPED_LOCK_SHARED;
if (m_needLoadFromFile) {
loadFromFile();
m_needLoadFromFile = false;
}
// 先从LRU缓存中查找
MMBuffer value;
if (m_lruCache.get(key, value)) {
return value;
}
// 从内存映射中查找
auto itr = m_dic.find(key);
if (itr != m_dic.end()) {
// 添加到LRU缓存
m_lruCache.put(key, itr->second);
return itr->second;
}
return MMBuffer();
}
};
九、常见问题与解决方案
9.1 数据读取失败
问题描述:调用MMKV读取数据返回默认值或失败。
可能原因:
- 键名错误
- 数据类型不匹配
- 数据文件损坏
- 加密密钥不匹配
解决方案:
- 检查键名是否正确
- 确保使用正确的数据类型读取方法
- 调用reload()方法重新加载数据
- 确保加密密钥一致
9.2 多进程数据不一致
问题描述:多进程环境下,不同进程读取到的数据不一致。
可能原因:
- 文件修改未及时同步
- 锁机制使用不当
- 缓存未及时更新
解决方案:
- 增加文件变化检测频率
- 确保正确使用多进程模式初始化MMKV
- 在适当的时候调用reload()方法刷新数据
9.3 读取性能问题
问题描述:大量数据读取时性能下降。
可能原因:
- 频繁的文件IO
- 数据解析开销大
- 锁竞争激烈
解决方案:
- 批量读取数据,减少IO次数
- 使用LRU缓存优化频繁访问的数据
- 考虑使用多线程并行读取