简称SP,使用键值对的形式保存原始类型的数据,默认以XML格式的文件来存储这些数据java
因为咱们目前使用的SP方案,咱们的痛点,性能差,和ANR问题,考虑换一种数据存储方案,首先咱们的数据存储的场景并非复杂数据类型,量上也不是太大,因此优先考虑的就是MMKV,为了更清楚的了解MMKV的优缺点,因此对比参考下android
名称 | MMKV | Realm | WCDB | Room |
---|---|---|---|---|
方案 | mmap | nosql | SQLCipher | SQLite |
版本 | 1.0.24 | 6.0.2 | 1.0.8 | 2.2.3 |
size | 0.15mb | 1.5mb | 0.7mb | 0.05mb |
迁移 | 从SP迁移方便 | 从SQLite迁移方便 | ||
场景 | 替换SP | 替换SQLite | 替换SQLite | 替换SQLite |
注: size 为armeabi-v7a架构下打包增量大小
mmkv包含lib_c++shared库的size为0.3mbc++
初始化 | MMKV | SP | Realm | Room | WCDB |
---|---|---|---|---|---|
耗时 | 30ms | 4ms | 60ms | 15ms | 80ms |
内存 | 1mb | 约1mb | 约2mb | 1mb | 2mb |
cpu峰值 | 16% | 20% | 18% | 15% | 15% |
SP | 执行次数 | 写入耗时 | 读取耗时 | 数据文件 |
---|---|---|---|---|
字符串 | 200 | 51ms | 4ms | 8kb |
字符串 | 1000 | 171ms | 6ms | 42kb |
Java实体 | 1000 | 220ms | 87ms | 172kb |
MMKV | 执行次数 | 写入耗时 | 读取耗时 | 数据文件 |
---|---|---|---|---|
字符串 | 200 | 8ms | 5ms | 4kb |
字符串 | 1000 | 20ms | 9ms | 16kb |
Java实体 | 1000 | 117ms | 93ms | 128kb |
Realm | 执行次数 | 写入耗时 | 读取耗时 | 数据文件 |
---|---|---|---|---|
字符串 | 200 | 229ms | 74ms | 32kb |
字符串 | 1000 | 606ms | 122ms | 288kb |
Java实体 | 1000 | 760ms | 130ms | 576kb |
Room | 执行次数 | 写入耗时 | 读取耗时 | 数据文件 |
---|---|---|---|---|
字符串 | 200 | 636ms | 160ms | 32kb |
字符串 | 1000 | 1350ms | 475ms | 48kb |
Java实体 | 1000 | 1298ms | 432ms | 68kb |
wcdb | 执行次数 | 写入耗时 | 读取耗时 | 数据文件 |
---|---|---|---|---|
字符串 | 200 | 636ms | 160ms | 32kb |
字符串 | 1000 | 1350ms | 475ms | 48kb |
Java实体 | 1000 | 1298ms | 432ms | 68kb |
内存消耗 | MMKV | SP |
---|---|---|
初始化 + 查询1k条 | 1.7mb | 1.2mb |
初始化 + 查询1w条 | 3.6mb | 2.8mb |
初始化 + 查询5w条 | 10.7mb(文件8mb) | 8.5mb(文件8.6mb) |
字符串数据:含有随机数的10个字节
运行手机:华为 Mate10
执行线程:子线程
统计方式:取3次平均值
内存消耗:业务代码读取对应数据后释放本地对象后的内存增量
因为序列化反序列化会有缓存,Java实体
SP写入为apply异步方式
Realm、WCDB、Room 的插入操做均为单条插入
MMKV、SP 读取和写入Java实体对象数据时间包含实体类序列化和反序列化时间,不过因为序列化反序列化会有缓存,时间会稍偏小git
protobuf
存储,SP使用xml
。protobuf
相比xml
来讲性能更好,体积也会减少,可是真实存储空间来看,mmkv的扩容机制(在空间不足时会申请一倍的空间)可能会致使实际使用空间变大,相比较SP的占用的存储空间就是真实的数据大小。MMKV 经过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由操做系统负责将内存回写到文件,没必要担忧 crash 致使数据丢失。而SP则 MMKV 是一个跨平台的解决方案,写入性能相比sp有很大提高,适用于须要一个通用的 key-value 存储组件,有跨进程访问需求,尤为是对如今sp存储方案的不满的同窗,mmkv同时支持了sp的迁移支持,基本上不用调整太多的代码,迁移事项下面会详述。github
Realm做为一款移动端的NoSQL框架,官方定位就是替代SQLite等关系型数据库,跨平台,性能优秀,有可视化的查看工具。不足也比较明显,不管是集成包size仍是数据文件都有点大。另外Realm要求当前Bean对象必须直接继承RealmObject,侵入性很是强。有线程限制,不能跨线程使用数据对象。另外我在写测试demo的时候发现了一个坑,若是一个线程查询出列表后,另外一个线程再去增长或删除会使得本地数据文件产生备份,致使数据库文件急速增加,达到几百MB,而且不会自动清理,官方对这个也有说明:sql
WCDB是一个高效、完整、易用的移动数据库框架,基于SQLCipher,支持iOS, macOS和Android数据库
Room并非一个数据库,他是在 SQLiteo 的基础上提供了一个抽象层,让用户可以在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。并保留了灵活的接口适配层。目前WCDB已经支持接入Room:github.com/Tencent/wcd…缓存
想尝试的同窗能够试试 ObjectBox,这个也是GreenDao做者的另外一力做,听说性能比Realm还要好,因为本次是对老项目中的SP存储方案选型替换,暂时先不对比ObjectBox了,后续有时间了完善下相关对比记录,有几篇文章你们能够参考
notes.devlabs.bg/realm-objec…
chejdj.github.io/android/201…安全
总结下吧,sp 和 mmkv 都是本身有一分内存级别的缓存,因此查询很是快,可是若是数据量过大,会形成内存溢出,mmkv官方也对这点有说明,不要超过100mb,其实对正常的数据来讲,确定到不了这个限制,若是30mb以上的数据,都要考虑使用数据库类型的存储了,上面测试数据对于数据库类型的存储不太公平,主要针对单次频繁插入查询的使用场景,数据库使用场景通常会多条数据经过事务批量写入,查询的时候也会查询一类数据集合。综上所述,目前采用mmkv替换sp的实现仍是有很大收益的,性能和跨进程上都有优点,而且避免了sp的ANR问题,体积上也没有加大多少;若是数据库方案,建议采用Room,或者在尝试下上面说的可是没在本次测试对比的objectbox。感受要注意的挺多,不过零零散散只整理了这么点...架构
迁移官方文档:github.com/Tencent/MMK…
MMKV 不支持 Serializable,可是能够考虑把 Serializable 的 java对象 转换成 byte 存入MMKV
MMKV初始存储空间4kb,不足的时扩容最少是一倍,而且为了效率在移除key value
不会减少磁盘存储空间,官方提供了kv.trim
方法去缩减磁盘存储空间,不过官方建议调用时机是有大量移除操做的时候执行,通常无需考虑存储文件增加问题,但这个时机很差掌握,不过感受能够按期清理一次
SP#getAll
SP#registerOnSharedPreferenceChangeListener
等方法不支持,调用会抛出运行时异常,须要注意。getAll
由于擦除了类型,因此不支持,感受这是mmkv相对较弱的地方,也为之后迁移其余存储方案增长了成本
一些 Android 设备(API level 19)在安装/更新 APK 时可能出错, 致使 libmmkv.so 找不到。而后就会遇到 java.lang.UnsatisfiedLinkError 之类的 crash。有个开源库 ReLinker 专门解决这个问题,不过如今还在支持19的应用应该很少了
对于历史项目SP数据可能比较庞大,这样直接使用官方提供的mmkv#importFromSharedPreferences
方法直接对一个文件进行迁移,会形成耗时过长的问题,目前Demo测试数据:测试机型华为Mate10,一个4MB左右的SP的文件迁移到MMKV大约耗时80ms左右,到了线上应该还会更长,因此为了不初次使用卡住的体验问题,这里提供两个解决思路:1.在使用前统一迁移,而且给予用户一个迁移数据的提示;2.在初次使用的时候后台静默迁移。目前咱们使用的是第二种思路,这种状况须要保证数据的准确性,若是有同窗有兴趣能够留言探讨
官方和网上各类对比测试文章