MMKV源码解析
基于version:1.2.5
目錄
1、初始化
2、獲取MMKV實例
1、MMKV內部構建
3、寫入:encode
在緩存中找到了
4、讀取:decode
5、其他:多進程、文件鎖等
1、初始化
鏈路:MMKV.java:initialize->jniInitialize→native-bridge.cpp:initializeMMKV->MMKV.cpp:initializeMMKV
- 通過Linux系統的互斥鎖、自己設置的標記為來保證:一個進程只初始化一次MMKV
- 保存根目錄為全局變量
- 通過傳入的串地址生成真正目標需要的目錄結構
2、獲取MMKV實例
鏈路:MMKV.java:mmkvWithID->getMMKVWithID->native-bridge.cpp:mmkvWithID->MMKV_Android.cpp:mmkvWithID
注??:不管是使用默認的defaultMMKV還是mmkvWithID都是最終都匯于一處
-
- 通過mmapID和relativePath拼接后md5得到mmap的key(項目中其實relativePath為null,所以mmapId就是我們傳入的fileName)
- 緩存中找到直接返回
-
如果緩存沒有就構建一個新的實例
其實從這里可以發現java層的MMKV實例只是一個殼,是否構建MMKV實例是在native層進行的
1、MMKV內部構建
在MMKV_Android.cpp進行了真正的MMKV構建,各種參數配置,這里我們關注兩個最關鍵的地方:m_metaFile、loadFromFile
文件映射:m_metaFile
鏈路:MMKV_Android.cpp:MMKV:m_metaFile:MemoryFile_Android.cpp:MemoryFile:reloadFromFile:mmap
首先驗證文件大小
- 文件小于最小單位或者不是最先文件單位的整數倍,則進行整理大小(處理成整數倍方便對應內存映射管理)
- 大小符合映射條件,直接進行mmap映射
我們來來看下第一種情況:
第二種情況省去了第一種情況的大小調整直接進行了映射
到這里文件就完成了通過mmap映射到內存,并拿到了映射的地址:m_ptr
載入數據:loadFromFile
映射完內存,把數據文件倒入到內存就可以開干了!
導入數據前會現檢驗一下是否映射完畢映射內存可用,否則會重復一下映射的過程,同時這里會進行數據校驗,其中包含文件大小和CRC校驗
- CRC:通過驗證則將文件數據讀入m_crcDigest進行記錄并將文件讀入到m_dic,不通則回調異常onMMKVCRCCheckFail清空緩存文件數據,此次寫入作廢,有效防止惡意攻擊
之后如果有回寫標示(是在后續encode確認空間夠用時標記)進行回寫數據去重??。
到這我們就基本完成了構建工作,并且有了m_dic,這塊內存緩存后期就能被我們所用進行快速的查詢。
3、寫入:encode
調用鏈路(以string為例):native-bridge.cpp:encodeString:set:MMKV_IO.cpp:setDataForKey
除了開始有一個簡單的判斷值是否有效外,最終就是setDataForKey的舞臺了
這里我們重點關注下不實用密鑰的情況,與有密鑰對大體一致,只是有密鑰對會有一些加密數據判斷處理
分兩種情況:在緩存中找到 or 未找到。最后更散列
在緩存中找到了
首先計算出key與value經過ProtocolBuffer(ProtocolBuffer編碼)編碼完了的長度并進行空間驗證,如果空間不足,進行擴容擴容
最后的寫入操作:doAppendDataWithKey
4、讀取:decode
調用鏈路:native-bridge.cpp:decodeString? :MMKV.cpp:getString
讀取相對來說很簡單
5、其他:多進程、文件鎖等
這里就不多說了,官方給了非常詳細的解答:MMKV for Android 多進程設計與實現
參考文獻:
存儲性能優化 MMKV源碼解析 - 簡書前言 好久沒有更新常用的第三方庫了。讓我們來聊聊MMKV這個常用的第三方庫。MMKV這個庫是做什么的呢?他本質上的定位和sp有點相似,經常用于持久化小數據的鍵值對。其速度可以...https://www.jianshu.com/p/c12290a9a3f7
總結
- 上一篇: MySQL单机版Recycle Bin回
- 下一篇: C++ 线程安全的单例模式