FastKV是用Java编写的高效可靠的key-value存储库。
可以用于各种有JVM环境的运行平台,比如Android。
FastKV有以下特点:
- 读写速度快
- 二进制编码,编码后的体积相对XML等文本编码要小很多;
- 增量编码:FastKV记录了各个key-value相对文件的偏移量(包括失效的key-value), 从而在更新数据时可以直接在指定的位置写入数据。
- 默认用mmap的方式记录数据,更新数据时直接写入到内存即可,没有IO阻塞。
- 支持多种写入模式
- 除了mmap这种非阻塞的写入方式,FastKV也支持常规的阻塞式写入方式, 并且支持同步阻塞和异步阻塞(分别类似于SharePreferences的commit和apply)。
- 支持多种类型
- 支持常用的boolean/int/float/long/double/String等基础类型。
- 支持ByteArray (byte[])。
- 支持存储自定义对象。
- 内置Set的编码器 (兼容SharePreferences)。
- 支持多进程
- 项目提供了支持多进程的存储类(MPFastKV)。
- 支持监听文件内容变化,其中一个进程修改文件,所有进程皆可感知。
- 方便易用
- FastKV提供了了丰富的API接口,开箱即用。
- 提供的接口其中包括getAll()和putAll()方法, 所以很方便迁移SharePreferences等框架的数据到FastKV, 当然,迁移FastKV的数据到其他框架也很方便。
- 稳定可靠
- 通过double-write等方法确保数据的完整性。
- 在API抛IO异常时提供降级处理。
- 代码精简
- FastKV由纯Java实现,编译成jar包后体积只有几十K。
FastKV 已发布到Maven中央仓库,分别发布了两个jar包,按需添加依赖即可。
其中一个包含封装了SharePreferences接口和支持多进程:
dependencies {
implementation 'io.github.billywei01:fastkv:1.1.2'
}
另一个仅Java接口(不包含Android SDK, 可在Java环境下调用),不支持多进程:
dependencies {
implementation 'io.github.billywei01:fastkv-java:1.1.0'
}
FastKVConfig.setLogger(FastKVLogger)
FastKVConfig.setExecutor(ChannelExecutorService(4))
初始化可以按需设置日志回调和Executor。 建议传入自己的线程池,以复用线程。
日志接口提供三个级别的回调,按需实现即可。
public interface Logger {
void i(String name, String message);
void w(String name, Exception e);
void e(String name, Exception e);
}
- 基本用法
FastKV kv = new FastKV.Builder(path, name).build();
if(!kv.getBoolean("flag")){
kv.putBoolean("flag" , true);
}
- 存储自定义对象
FastKV.Encoder<?>[] encoders = new FastKV.Encoder[]{LongListEncoder.INSTANCE};
FastKV kv = new FastKV.Builder(path, name).encoder(encoders).build();
List<Long> list = new ArrayList<>();
list.add(100L);
list.add(200L);
list.add(300L);
kv.putObject("long_list", list, LongListEncoder.INSTANCE);
List<Long> list2 = kv.getObject("long_list");
除了支持基本类型外,FastKV还支持写入对象,只需在构建FastKV实例时传入对象的编码器即可。 编码器为实现FastKV.Encoder的对象。 比如上面的LongListEncoder的实现如下:
public class LongListEncoder implements FastKV.Encoder<List<Long>> {
public static final LongListEncoder INSTANCE = new LongListEncoder();
@Override
public String tag() {
return "LongList";
}
@Override
public byte[] encode(List<Long> obj) {
return new PackEncoder().putLongList(0, obj).getBytes();
}
@Override
public List<Long> decode(byte[] bytes, int offset, int length) {
PackDecoder decoder = PackDecoder.newInstance(bytes, offset, length);
List<Long> list = decoder.getLongList(0);
decoder.recycle();
return (list != null) ? list : new ArrayList<>();
}
}
编码对象涉及序列化/反序列化。
这里推荐笔者的另外一个框架:https://github.com/BillyWei01/Packable
相对于常规用法,Android平台主要是多了SharePreferences API, 以及支持Kotlin,支持多进程等。
具体参考:用法 For Android
- 测试数据:搜集APP中的SharePreferenses汇总的部份key-value数据(经过随机混淆)得到总共四百多个key-value。
由于日常使用过程中部分key-value访问多,部分访问少,所以构造了一个正态分布的访问序列。
此过程循环多遍,分别求其总耗时。 - 比较对象: SharePreferences/DataStore/MMKV。
- 测试机型:荣耀20S。
测试代码:Benchmark
测试结果:
写入(ms) | 读取(ms) | |
---|---|---|
SharePreferences | 1182 | 2 |
DataStore | 33277 | 2 |
MMKV | 29 | 10 |
FastKV | 19 | 1 |
- SharePreferences提交用的是apply, 耗时依然不少。
- DataStore写入很慢。
- MMKV的读取比SharePreferences/DataStore要慢一些,写入则比之快许多。
- FastKV无论读取还是写入都比其他方式要快。
现实中通常情况下不会有此差距,因为一般而言不会几百个key-value写到同一个文件,此处仅为显示极端情况下的对比,读者可自行调整参数看对比数据。
参考:实现要点
See the LICENSE file for license rights and limitations.