MMKV学习和使用
MMKV介紹
MMKV 是騰訊開源的一款基于 mmap 內存映射的 key-value 組件,底層序列化/反序列化使用 protobuf 實現,性能高,穩定性強,從 2015 年中至今在微信上使用,其性能和穩定性經過了時間的驗證。
GitHub地址:https://github.com/Tencent/MMKV
為什么要替代SharedPreferences?
1,數據加密。 在 Android 環境里,數據加密是非常必須的,SP實際上是把鍵值對放到本地文件中進行存儲。如果要保證數據安全需要自己加密,MMKV 使用了 AES CFB-128 算法來加密/解密。
2,多進程共享。系統自帶的 SharedPreferences 對多進程的支持不好。現有基于 ContentProvider 封裝的實現,雖然多進程是支持了,但是性能低下,經常導致 ANR。考慮到 mmap 共享內存本質上是多進程共享的,MMKV 在這個基礎上,深入挖掘了Android 系統的能力,提供了可能是業界最高效的多進程數據共享組件。
3,匿名內存。 在多進程共享的基礎上,考慮到某些敏感數據(例如密碼)需要進程間共享,但是不方便落地存儲到文件上,直接用 mmap 不合適。而Android 系統提供了 Ashmem 匿名共享內存的能力,它在 進程退出后就會消失,不會落地到文件上,非常適合這個場景。MMKV 基于此也提供了 Ashmem(匿名共享內存) MMKV 的功能。
4,效率更高。MMKV 使用protobuf進行序列化和反序列化,比起SP的xml存放方式,更加高效。
5,支持從 SP遷移,如果你之前項目里面都是使用SP,現在想改為使用MMKV,只需幾行代碼即可將之前的SP實現遷移到MMKV。
支持的數據類型
1,支持以下 Java 語言基礎類型:
???? boolean、int、long、float、double、byte[]
2,支持以下 Java 類和容器:
??? String、Set< String >
? ? 任何實現了Parcelable的類型
使用
1,添加依賴
mmkv從1.0.20版本開始遷移到AndroidX,具體版本歷史請參看 CHANGELOG.md。
dependencies { compile 'com.tencent:mmkv:1.0.23' }2,初始化
在自定義的Application中:
MMKV.initialize(this);3,數據操作(工具類封裝)
MMKV 提供一個全局的實例,可以直接使用: import com.tencent.mmkv.MMKV;...MMKV kv = MMKV.defaultMMKV(); // 存儲數據很簡單了,只需要調用如下一行代碼即可,不用再如prefences一樣調用apply或commit: 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: "+newString(kv.decodeBytes("bytes")));4,刪除 & 查詢:
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.clearAll();boolean hasBool = kv.containsKey("bool");5,如果不同業務需要區別存儲,也可以單獨創建自己的實例:
MMKV mmkv = MMKV.mmkvWithID("MyID"); mmkv.encode("bool", true);6,默認是支持單進程的,如果業務需要多進程訪問,那么在初始化的時候加上標志位 MMKV.MULTI_PROCESS_MODE:
MMKV mmkv = MMKV.mmkvWithID("InterProcessKV", MMKV.MULTI_PROCESS_MODE); mmkv.encode("bool", true);自定義根目錄
MMKV 默認把文件存放在$(FilesDir)/mmkv/目錄。你可以在 App 啟動時自定義根目錄:
String dir = getFilesDir().getAbsolutePath() + "/mmkv_2"; String rootDir = MMKV.initialize(dir); Log.i("MMKV", "mmkv root: " + rootDir);MMKV 甚至支持自定義某個文件的目錄:
String relativePath = getFilesDir().getAbsolutePath() + "/mmkv_3"; MMKV kv = MMKV.mmkvWithID("testCustomDir", relativePath);SharedPreferences 遷移
MMKV 提供了 importFromSharedPreferences() 函數,可以比較方便地遷移數據過來。
MMKV 還額外實現了一遍 SharedPreferences、SharedPreferences.Editor 這兩個 interface,在遷移的時候只需兩三行代碼即可,其他 CRUD 操作代碼都不用改。
private void testImportSharedPreferences() { //SharedPreferences preferences = getSharedPreferences("myData",MODE_PRIVATE);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();}工具類封裝如下:
public class SpUtils {private static SpUtils mInstance;private static MMKV mv;private SpUtils2() {mv = MMKV.defaultMMKV();}/*** 初始化MMKV,只需要初始化一次,建議在Application中初始化*/public static SpUtils getInstance() {if (mInstance == null) {synchronized (SpUtils.class) {if (mInstance == null) {mInstance = new SpUtils();}}}return mInstance;}/*** 保存數據的方法,我們需要拿到保存數據的具體類型,然后根據類型調用不同的保存方法** @param key* @param object*/public static void encode(String key, Object object) {if (object instanceof String) {mv.encode(key, (String) object);} else if (object instanceof Integer) {mv.encode(key, (Integer) object);} else if (object instanceof Boolean) {mv.encode(key, (Boolean) object);} else if (object instanceof Float) {mv.encode(key, (Float) object);} else if (object instanceof Long) {mv.encode(key, (Long) object);} else if (object instanceof Double) {mv.encode(key, (Double) object);} else if (object instanceof byte[]) {mv.encode(key, (byte[]) object);} else {mv.encode(key, object.toString());}}public static void encodeSet(String key, Set<String> sets) {mv.encode(key, sets);}public static void encodeParcelable(String key, Parcelable obj) {mv.encode(key, obj);}/*** 得到保存數據的方法,我們根據默認值得到保存的數據的具體類型,然后調用相對于的方法獲取值*/public static Integer decodeInt(String key) {return mv.decodeInt(key, 0);}public static Double decodeDouble(String key) {return mv.decodeDouble(key, 0.00);}public static Long decodeLong(String key) {return mv.decodeLong(key, 0L);}public static Boolean decodeBoolean(String key) {return mv.decodeBool(key, false);}public static Float decodeFloat(String key) {return mv.decodeFloat(key, 0F);}public static byte[] decodeBytes(String key) {return mv.decodeBytes(key);}public static String decodeString(String key) {return mv.decodeString(key, "");}public static Set<String> decodeStringSet(String key) {return mv.decodeStringSet(key, Collections.<String>emptySet());}public static Parcelable decodeParcelable(String key) {return mv.decodeParcelable(key, null);}/*** 移除某個key對** @param key*/public static void removeKey(String key) {mv.removeValueForKey(key);}/*** 清除所有key*/public static void clearAll() {mv.clearAll();}}OK搞定
總結
- 上一篇: GooFlow获取节点/线信息和自定义节
- 下一篇: C++ 11 nullptr关键字