MMKV 原理以及使用
介紹
MMKV是基于mmap內存映射的移動端通用key-value組件,底層序列化/反序列化使用protobuf實現,性能高,穩定性強。從2015年中至今,在iOS微信上使用已有近3年,近期移植到Android平臺,移動端全平臺通用,并全部在Github上開源。
MMKV 原理
內存準備:
通過 mmap 內存映射文件,提供一段可供隨時寫入的內存塊,App 只管往里面寫數據,由操作系統負責將內存回寫到文件,不必擔心 crash 導致數據丟失。
數據組織:
數據序列化方面我們選用 protobuf 協議,pb 在性能和空間占用上都有不錯的表現。考慮到我們要提供的是通用 kv 組件,key 可以限定是 string 字符串類型,value 則多種多樣(int/bool/double 等)。要做到通用的話,考慮將 value 通過 protobuf 協議序列化成統一的內存塊(buffer),然后就可以將這些 KV 對象序列化到內存中。
寫入優化: (重點關注這里!!!)
標準 protobuf 不提供增量更新的能力,每次寫入都必須全量寫入。考慮到主要使用場景是頻繁地進行寫入更新,我們需要有增量更新的能力:將增量 kv 對象序列化后,直接 append 到內存末尾;這樣同一個 key 會有新舊若干份數據,最新的數據在最后;那么只需在程序啟動第一次打開 mmkv 時,不斷用后讀入的 value 替換之前的值,就可以保證數據是最新有效的。
空間增長: (還有這里!!!)
使用 append 實現增量更新帶來了一個新的問題,就是不斷 append 的話,文件大小會增長得不可控。例如同一個 key 不斷更新的話,是可能耗盡幾百 M 甚至上 G 空間,而事實上整個 kv 文件就這一個 key,不到 1k 空間就存得下。這明顯是不可取的。我們需要在性能和空間上做個折中:以內存 pagesize 為單位申請空間,在空間用盡之前都是 append 模式;當 append 到文件末尾時,進行文件重整、key 排重,嘗試序列化保存排重結果;排重后空間還是不夠用的話,將文件擴大一倍,直到空間足夠。
數據有效性:
考慮到文件系統、操作系統都有一定的不穩定性,我們另外增加了 crc 校驗,對無效數據進行甄別。在 iOS 微信現網環境上,我們觀察到有平均約 70萬日次的數據校驗不通過。
MMKV 使用
依賴:
implementation 'com.tencent:mmkv:1.0.19'在Application里面初始化:
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);String rootDir = MMKV.initialize(this);//就這么一句話就行System.out.println("mmkv root: " + rootDir); }支持的數據類型:
支持以下 Java 語言基礎類型:
boolean、int、long、float、double、byte[],String、Set<String>,任何實現了Parcelable的類型,對象存儲方式是,轉化成json串,通過字符串存儲,使用的時候在取出來反序列化.
增:
MMKV kv = MMKV.defaultMMKV();kv.encode("bool", true); System.out.println("bool: " + kv.decodeBool("bool"));kv.encode("int", Integer.MIN_VALUE); System.out.println("int: " + kv.decodeInt("int"));kv.encode("long", Long.MAX_VALUE); System.out.println("long: " + kv.decodeLong("long"));kv.encode("float", -3.14f); System.out.println("float: " + kv.decodeFloat("float"));kv.encode("double", Double.MIN_VALUE); System.out.println("double: " + kv.decodeDouble("double"));kv.encode("string", "Hello from mmkv"); System.out.println("string: " + kv.decodeString("string"));byte[] bytes = {'m', 'm', 'k', 'v'}; kv.encode("bytes", bytes); System.out.println("bytes: " + new String(kv.decodeBytes("bytes")));注意:mmkv的寫入邏輯是:當我們覆蓋某個值的時候,它并不會立即刪除前面的值,會保留,然后每個key,value有存儲限制,當觸發存儲限制的時候,才會執行刪除,這樣即使我們頻繁的覆蓋,也不會引起太多的性能損耗
刪:
MMKV kv = MMKV.defaultMMKV();kv.removeValueForKey("bool"); System.out.println("bool: " + kv.decodeBool("bool"));kv.removeValuesForKeys(new String[]{"int", "long"}); System.out.println("allKeys: " + Arrays.toString(kv.allKeys()));改:
直接在存一遍就是.(執行增步驟)
查:
在增的步驟里面,已經打印可查的結果.
kv.decodeBool("bool");kv.decodeInt("int"); .....如果不同業務需要區別存儲,也可以單獨創建自己的實例:
MMKV* mmkv = MMKV.mmkvWithID("MyID"); mmkv.encode("bool", true);SharedPreferences 遷移
MMKV preferences = MMKV.mmkvWithID("myData");// 遷移舊數據{SharedPreferences old_man = getSharedPreferences("myData", MODE_PRIVATE);preferences.importFromSharedPreferences(old_man);old_man.edit().clear().commit();}// 跟以前用法一樣SharedPreferences.Editor editor = preferences.edit();editor.putBoolean("bool", true);editor.putInt("int", Integer.MIN_VALUE);editor.putLong("long", Long.MAX_VALUE);editor.putFloat("float", -3.14f);editor.putString("string", "hello, imported");HashSet<String> set = new HashSet<String>();set.add("W"); set.add("e"); set.add("C"); set.add("h"); set.add("a"); set.add("t");editor.putStringSet("string-set", set);// 無需調用 commit()//editor.commit();以上內容來自官方github:https://github.com/Tencent/MMKV/wiki/android_setup_cn
作者:鵝鵝鵝曲項向天歌呀
鏈接:https://www.jianshu.com/p/fe8a827ceffe
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
以上是生活随笔為你收集整理的MMKV 原理以及使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql基本语法 外键_MySQL语法
- 下一篇: 文件断点续传原理与实现