微信开源库MMKV遍历读取存储的所有key以及对应的value方法

微信开源库MMKV遍历读取存储的所有key以及对应的value方法

最近正在使用微信的开源库MMKV,替代项目中已存在的sharePreferences,替换过程非常简单,使用MMKV的导入SP接口直接把SP里面的数据全部转移到MMKV中。

项目中存在一个测试工具,读取SP文件里面的所有key以及对应的value在recyclerView中进行展示,方便查看和修改。在更新MMKV之后由于存储方式的改变,无法通过旧代码读取文件遍历所有数据,需要更新遍历的逻辑。

MMKV提供读取所有key集合的api:mmkv.allKeys()
通过遍历所有key读取value

到了decode这一步,需要判断value的类型确定使用哪个api进行decode数据。由于MMKV存储格式原因,读取value的时候无法像以前SP那样直接读出Object然后进行类型判断。在MMKV里面不管encode什么类型的数据,通过其他类型的api decode时都不会出现异常,也不会读取出错返回default值,所以判断类型这一步,需要观察数据读取的规律进行逻辑判断。

对各种基本类型数据进行encode并通过不同的decode读取

string-set: [1, 2, 3]
D/PrefEditFragment: testMMKV: decodeInt 6
D/PrefEditFragment: testMMKV: decodeFloat 3.25105E-38
D/PrefEditFragment: testMMKV: decodeDouble 1.057169819026035E-307
D/PrefEditFragment: testMMKV: decodeLong 6
D/PrefEditFragment: testMMKV: decodeBool true
D/PrefEditFragment: testMMKV: decodeString <0x01>1<0x01>2<0x01>3
D/PrefEditFragment: testMMKV: decodeBytes <0x01>1<0x01>2<0x01>3
D/PrefEditFragment: testMMKV: decodeStringSet [1, 2, 3]

int: 100
D/PrefEditFragment: testMMKV: decodeInt 100
D/PrefEditFragment: testMMKV: decodeFloat 1.4E-43
D/PrefEditFragment: testMMKV: decodeDouble 4.94E-322
D/PrefEditFragment: testMMKV: decodeLong 100
D/PrefEditFragment: testMMKV: decodeBool true
D/PrefEditFragment: testMMKV: decodeString 
D/PrefEditFragment: testMMKV: decodeBytes 
D/PrefEditFragment: testMMKV: decodeStringSet null

long: 100
D/PrefEditFragment: testMMKV: decodeInt 100
D/PrefEditFragment: testMMKV: decodeFloat 1.4E-43
D/PrefEditFragment: testMMKV: decodeDouble 4.94E-322
D/PrefEditFragment: testMMKV: decodeLong 100
D/PrefEditFragment: testMMKV: decodeBool true
D/PrefEditFragment: testMMKV: decodeString 
D/PrefEditFragment: testMMKV: decodeBytes 
D/PrefEditFragment: testMMKV: decodeStringSet null

float: 0
D/PrefEditFragment: testMMKV: decodeInt 0
D/PrefEditFragment: testMMKV: decodeFloat 0.0
D/PrefEditFragment: testMMKV: decodeDouble 0.0
D/PrefEditFragment: testMMKV: decodeLong 0
D/PrefEditFragment: testMMKV: decodeBool false
D/PrefEditFragment: testMMKV: decodeString 
D/PrefEditFragment: testMMKV: decodeBytes 
D/PrefEditFragment: testMMKV: decodeStringSet []

long: Long.MAX_VALUE-1
D/PrefEditFragment: testMMKV: decodeInt -2
D/PrefEditFragment: testMMKV: decodeFloat NaN
D/PrefEditFragment: testMMKV: decodeDouble NaN
D/PrefEditFragment: testMMKV: decodeLong 9223372036854775806
D/PrefEditFragment: testMMKV: decodeBool true
D/PrefEditFragment: testMMKV: decodeString 
D/PrefEditFragment: testMMKV: decodeBytes 
D/PrefEditFragment: testMMKV: decodeStringSet null

double: 100d
D/PrefEditFragment: testMMKV: decodeInt 0
D/PrefEditFragment: testMMKV: decodeFloat 0.0
D/PrefEditFragment: testMMKV: decodeDouble 100.0
D/PrefEditFragment: testMMKV: decodeLong 0
D/PrefEditFragment: testMMKV: decodeBool false
D/PrefEditFragment: testMMKV: decodeString 
D/PrefEditFragment: testMMKV: decodeBytes 
D/PrefEditFragment: testMMKV: decodeStringSet []

float: 100f
D/PrefEditFragment: testMMKV: decodeInt 0
D/PrefEditFragment: testMMKV: decodeFloat 100.0
D/PrefEditFragment: testMMKV: decodeDouble 5.53552857E-315
D/PrefEditFragment: testMMKV: decodeLong 0
D/PrefEditFragment: testMMKV: decodeBool false
D/PrefEditFragment: testMMKV: decodeString 
D/PrefEditFragment: testMMKV: decodeBytes 
D/PrefEditFragment: testMMKV: decodeStringSet []

bool: true
D/PrefEditFragment: testMMKV: decodeInt 1
D/PrefEditFragment: testMMKV: decodeFloat 1.4E-45
D/PrefEditFragment: testMMKV: decodeDouble 4.9E-324
D/PrefEditFragment: testMMKV: decodeLong 1
D/PrefEditFragment: testMMKV: decodeBool true
D/PrefEditFragment: testMMKV: decodeString 
D/PrefEditFragment: testMMKV: decodeBytes 
D/PrefEditFragment: testMMKV: decodeStringSet null

bool: false
D/PrefEditFragment: testMMKV: decodeInt 0
D/PrefEditFragment: testMMKV: decodeFloat 0.0
D/PrefEditFragment: testMMKV: decodeDouble 0.0
D/PrefEditFragment: testMMKV: decodeLong 0
D/PrefEditFragment: testMMKV: decodeBool false
D/PrefEditFragment: testMMKV: decodeString 
D/PrefEditFragment: testMMKV: decodeBytes 
D/PrefEditFragment: testMMKV: decodeStringSet null

string: 嘿嘿嘿
D/PrefEditFragment: testMMKV: decodeInt 9
D/PrefEditFragment: testMMKV: decodeFloat -1.1944896
D/PrefEditFragment: testMMKV: decodeDouble -1.3111330438961331E182
D/PrefEditFragment: testMMKV: decodeLong 9
D/PrefEditFragment: testMMKV: decodeBool true
D/PrefEditFragment: testMMKV: decodeString 嘿嘿嘿
D/PrefEditFragment: testMMKV: decodeBytes 嘿嘿嘿
D/PrefEditFragment: testMMKV: decodeStringSet []

根据上面的数据读取规律,优先处理stringstring-set类型,因为只有stringstring-set类型encode才能通过同样的decode读出来,而其他类型通过string进行decode出来的都是空字符串。再观察stringstring-set类型的区别,可以看到decode成stringstring-set数据是包含<0x01>作为分隔符的。当字符串以<0x01>开头时,我们可以把类型解析为string-set,否则解析为string,如果encode的字符串确实以<0x01>开头,那就要开发者根据自己的业务规则自行处理了。

继续根据数据读取规律,接下来把 int、long、bool 类型放到一起处理,这里可以看到 int 类型10等价于 bool 类型truefalse。所以bool类型可以等价于int类型进行处理。然后看int类型与long类型,它们分别是32位和64位,所以当值是小于32位时,可以直接当成int读取,因为两种类型的值是相等的;当两种类型的值不相等时,说明值超出了32位的范围,可以直接用long类型读取。

最后剩下floatdouble类型,这两种类型就比较难处理了,在处理这两种数据类型之前,先去了解了float类型和double类型的二进制存储,这里简单记录一下:
float类型是32位的,它的组成是 1个符号位,8个阶码位,23个尾数位
double类型是64位的,它的组成是 1个符号位,11个阶码位,52个尾数位

举个栗子,存储一个 100f 的float数据,把它转成byte[]的形式看,表示4个字节[66, -56, 0, 0]
而把这个 100f 的float数据转成double类型的byte[]形式,表示8个字节[0, 0, 0, 0, 66, -56, 0, 0]
可以看到当一个float类型数据通过double类型读出时,数据存储在64位8个字节中的后32位。
由于float类型只有32位,所以无论任何时候一个float数据读到8个字节里面,前面32位均为0。但条件反过来,把数据读到64位8个字节里,前32位均为0并不代表这个数据就是float数据。
double类型的数据处于[0, 0, 0, 0, 0, 0, 0, 0] ~ [0, 0, 0, 0, -127, -127, -127, -127]的时候,我们无法判断这是一个float类型数据还是double类型数据。

//这个数据范围用指数的形式表示就是 
0 < value <= 1.0569021313E-314
//说人话,数据范围大概是 
0 < value <= 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105690213213d

只要存储的double类型数据不在这个范围,那就可以用上面提到的条件进行类型判断:把数据用double类型读出来,前32位均为0就把它当做float数据。(如果存储的数据确实在上面说的范围内,则需要根据业务逻辑自行处理类型判断)

自动判断类型并获取value的方法代码:
private Object getObjectValue(MMKV mmkv, String key) {
    // 因为其他基础类型value会读成空字符串,所以不是空字符串即为string or string-set类型
    String value = mmkv.decodeString(key);
    if (!TextUtils.isEmpty(value)) {
        // 判断 string or string-set
        if (value.charAt(0) == 0x01) {
            return mmkv.decodeStringSet(key);
        } else {
            return value;
        }
    }
    // float double类型可通过string-set配合判断
    // 通过数据分析可以看到类型为float或double时string类型为空字符串且string-set类型读出空数组
    // 最后判断float为0或NAN的时候可以直接读成double类型,否则读float类型
    // 该判断方法对于非常小的double类型数据 (0d < value <= 1.0569021313E-314) 不生效
    Set<String> set = mmkv.decodeStringSet(key);
    if (set != null && set.size() == 0) {
        Float valueFloat = mmkv.decodeFloat(key);
        Double valueDouble = mmkv.decodeDouble(key);
        if (Float.compare(valueFloat, 0f) == 0 || Float.compare(valueFloat, Float.NaN) == 0) {
            return valueDouble;
        } else {
            return valueFloat;
        }
    }
    // int long bool 类型的处理放在一起, int类型1和0等价于bool类型true和false
    // 判断long或int类型时, 如果数据长度超出int的最大长度, 则long与int读出的数据不等, 可确定为long类型
    int valueInt = mmkv.decodeInt(key);
    long valueLong = mmkv.decodeLong(key);
    if (valueInt != valueLong) {
        return valueLong;
    } else {
        return valueInt;
    }
}

你可能感兴趣的:(android)