MMKV改造-你可能会后悔

1,770 阅读1分钟

首先,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<>();
    }
}

其他

Github仓库