在使用MMKV框架前,需調用如下方法進行初始化bash
MMKV.initialize(context);
複製代碼
這裏的Java層主要是獲取到保存文件的路徑,傳入Native層,這裏默認的路徑是APP的內部存儲目錄下的mmkv路徑,這裏不支持修改,如需修改,需將源碼clone下來手動修改編譯了。app
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對象的方法有如下幾個,最傻瓜式的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裏ui
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的構造函數裏,作了一系列參數的構造,分別有:spa
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裏code
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);
}
複製代碼
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的讀寫原理等。 若是本文對您有用的話,記得點一個贊哦!