MMKV——基于 mmap 的高性能通用 key-value 组件
程序员文章站
2022-04-21 10:22:50
MMKV 原理内存准备通过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由操作系统负责将内存回写到文件,不必担心 crash 导致数据丢失。数据组织数据序列化方面我们选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现。写入优化考虑到主要使用场景是频繁地进行写入更新,我们需要有增量更新的能力。我们考虑将增量 kv 对象序列化后,append 到内存末尾。空间增长使用 append 实现增量更新带来...
MMKV 原理
-
内存准备
通过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由操作系统负责将内存回写到文件,不必担心 crash 导致数据丢失。 -
数据组织
数据序列化方面我们选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现。 -
写入优化
考虑到主要使用场景是频繁地进行写入更新,我们需要有增量更新的能力。我们考虑将增量 kv 对象序列化后,append 到内存末尾。 -
空间增长
使用 append 实现增量更新带来了一个新的问题,就是不断 append 的话,文件大小会增长得不可控。我们需要在性能和空间上做个折中。
dependencies {
implementation 'com.tencent:mmkv:1.0.23'
}
MMKV 的使用非常简单,所有变更立马生效,无需调用 sync
、apply
。 在 App 启动时初始化 MMKV,设定 MMKV 的根目录(files/mmkv/),
public class APP extends Application {
@Override
public void onCreate() {
super.onCreate();
String rootDir = MMKV.initialize(this);
System.out.println("mmkv root: " + rootDir);
}
}
MMKV 提供一个全局的实例,可以直接使用:
import com.tencent.mmkv.MMKV;
//……
MMKV kv = MMKV.defaultMMKV();
kv.encode("bool", true);
boolean bValue = kv.decodeBool("bool");
kv.encode("int", Integer.MIN_VALUE);
int iValue = kv.decodeInt("int");
kv.encode("string", "Hello from mmkv");
String str = kv.decodeString("string");
最后贴个封装类:
import android.content.Context;
import android.os.Parcelable;
import com.tencent.mmkv.MMKV;
import java.util.Collections;
import java.util.Set;
/**
* @package name:com.sunyard.myapplication
* @describe
* @anthor jokerlover
* @email:shengj.chen@sunyard.com
* @time 2020/9/22 3:09 PM
*/
public class SpUtils {
private volatile static SpUtils mInstance;
private static MMKV mv;
private SpUtils(Context context) {
MMKV.initialize(context);
mv = MMKV.defaultMMKV();
}
/**
* 初始化MMKV,只需要初始化一次,建议在Application中初始化
*
*/
public static SpUtils getInstance(Context context) {
if (mInstance == null) {
synchronized (SpUtils.class) {
if (mInstance == null) {
mInstance = new SpUtils(context);
}
}
}
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();
}
}
本文地址:https://blog.csdn.net/csj731742019/article/details/108733757
上一篇: 如何使得Unity Terrain(地形)的高度匹配道路模型的高度
下一篇: mysql 主键自增