首先,mmkv是鼎鼎大名的key value存储开源库,解决Android sp性能、多进程等问题,并能保障sp快速迁移到mmkv,使用便捷,备受开发者青睐。
但是mmkv也有一些小缺陷,如果不改造上线后你可能会遇到很多问题。
这篇文章不介绍原理,原理网上开源的文章也很多。
监听事件
mmkv不支持修改通知。我们可以上层封装mmkv为sharedpreferences
,使用和sp同样的接口实现数据交互。对于多进程类型可以通过广播通知。
MMKVSharedPreferences {
edit()
class Editer {
apply() {
for (Map.Entry<String, Object> entry : keyValues.entrySet()) {
notify()
}
}
}
}
存储类型
非常重要
mmkv提供多种方法保存数据支持int、String、double. 但是不管保存还是写入,都必须强指定类型,加上底层是protbuf,所以是通过指定类型去读取数据的位数。
没有实现数据类型,导致mmkv无法getAll
,当你后续想把mmkv迁移到其他存储框架将无法实现
, 这个是非常坑的点,一旦你初期迁移没有实现类型兼容处理,后续将非常痛苦。
解决方案
新增一个存储文件,记录mmkv值类型
for (Map.Entry<String, Object> entry : keyValues.entrySet()) {
Object value = entry.getValue();
if (value == null) {
mmkvType.remove(entry.getKey());
mmkv.remove(entry.getKey());
} else if (value instanceof Integer) {
if (!mmkvType.contains(entry.getKey())) mmkvType.encode(entry.getKey(), TYPE_INT);
mmkv.encode(entry.getKey(), (Integer) value);
} else if (value instanceof String) {
if (!mmkvType.contains(entry.getKey())) mmkvType.encode(entry.getKey(), TYPE_STRING);
mmkv.encode(entry.getKey(), (String) value);
} else if (value instanceof Long) {
if (!mmkvType.contains(entry.getKey())) mmkvType.encode(entry.getKey(), TYPE_LONG);
mmkv.encode(entry.getKey(), (Long) value);
} else if (value instanceof Float) {
if (!mmkvType.contains(entry.getKey())) mmkvType.encode(entry.getKey(), TYPE_FLOAT);
mmkv.encode(entry.getKey(), (Float) value);
} else if (value instanceof Boolean) {
if (!mmkvType.contains(entry.getKey())) mmkvType.encode(entry.getKey(), TYPE_BOOL);
mmkv.encode(entry.getKey(), (Boolean) value);
} else if (value instanceof Set) {
if (!mmkvType.contains(entry.getKey())) mmkvType.encode(entry.getKey(), TYPE_SET);
mmkv.encode(entry.getKey(), (Set<String>) value);
} else if (value instanceof MMKVEdit) {
mmkvType.remove(entry.getKey());
mmkv.remove(entry.getKey());
}
notifyListener(entry.getKey());
}
getAll通过先获取all keys, 再通过获取value类型后通过对应getInt
getString
等方法获取数据
public Map<String, ?> getAll() {
MMKV mmkvType = getMmkvType();
String[] allKeys = mmkv.allKeys();
if (allKeys != null && allKeys.length > 0) {
HashMap<String, Object> all = new HashMap<>();
for (String key : allKeys) {
int type = mmkvType.getInt(key, TYPE_UNKNOW);
Object value = null;
if (type != TYPE_UNKNOW) {
switch (type) {
case TYPE_INT:
value = mmkv.getInt(key, 0);
break;
case TYPE_LONG:
value = mmkv.getLong(key, 0);
break;
case TYPE_BOOL:
value = mmkv.getBoolean(key, false);
break;
case TYPE_STRING:
value = mmkv.getString(key, null);
break;
case TYPE_SET:
value = mmkv.getStringSet(key, null);
break;
case TYPE_FLOAT:
value = mmkv.getFloat(key, 0f);
break;
}
}
if (value != null) {
all.put(key, value);
}
}
return all;
} else {
return new HashMap<>();
}
}