最近正在使用微信的开源库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 []
根据上面的数据读取规律,优先处理string
和string-set
类型,因为只有string
和string-set
类型encode才能通过同样的decode读出来,而其他类型通过string
进行decode出来的都是空字符串。再观察string
和string-set
类型的区别,可以看到decode成string
的string-set
数据是包含<0x01>
作为分隔符的。当字符串以<0x01>
开头时,我们可以把类型解析为string-set
,否则解析为string
,如果encode的字符串确实以<0x01>
开头,那就要开发者根据自己的业务规则自行处理了。
继续根据数据读取规律,接下来把 int、long、bool
类型放到一起处理,这里可以看到 int
类型1
和0
等价于 bool
类型true
和false
。所以bool
类型可以等价于int
类型进行处理。然后看int
类型与long
类型,它们分别是32位和64位,所以当值是小于32位时,可以直接当成int
读取,因为两种类型的值是相等的;当两种类型的值不相等时,说明值超出了32位的范围,可以直接用long
类型读取。
最后剩下float
和double
类型,这两种类型就比较难处理了,在处理这两种数据类型之前,先去了解了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
数据。(如果存储的数据确实在上面说的范围内,则需要根据业务逻辑自行处理类型判断)
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;
}
}