Flutter的键值存储数据库使用示例详解
作者:编程之路从0到1 发布时间:2023-10-15 02:13:15
Flutter 键值存储数据库
键值存储是开发中十分常见的需求,在Flutter开发中,一般使用 shared_preferences 插件来实现。shared_preferences 本质上就是将键值对保存到一个XML文件中进行持久化。
而shared_preferences 实际上存在一定缺陷,譬如其性能较差,不适合处理大量数据,不能创建新的XML文件,所有数据存在同一个文件中。除此外,还有其他一些持久化方案,如SQLite、Hive等。
SQLite是关系型数据库,使用起来相对繁琐;而Hive是用Dart实现的一个轻量级键值对数据库,它使用简单,但同样性能较差,而且在储存大量数据时,更加耗费内存,因为它是一次性将所有数据读取到内存中,这在移动端不是很可取。当然,如果你只是用来存储几个简单的配置项数据,那也够用了。
unqlite
以上概述了一下Flutter的键值持久化方案,那么接下来就隆介绍一下本文推荐的Flutter键值存储方案——一个轻量级嵌入式nosql数据库unqlite!
unqlite是一个嵌入式的数据库,它实现了一个独立的、无服务器、零配置、事务性的nosql数据库引擎。它是一个文档存储数据库,类似于MongoDB, Redis, CouchDB等,同时也是一个标准的key/value存储数据库,类似于BerkeleyDB, LevelDB, 等。
首先unqlite是一个无服务器的数据库,这意味着它的通讯是直接读取数据库文件的(从磁盘读取),没有中间服务器进程。
然后unqlite也不像其他服务器一样,需要安装、配置以及权限管理等。它是直接嵌入到我们的程序中的
unqlite是一个单一数据库文件,它的数据库是一个普通的磁盘文件。这就意味着你可以把它随意的复制或备份到其他地方。
unqlite文件格式是跨平台的。在一台机器上编写的数据库文件可以复制到具有不同体系结构的不同机器上并在其上使用。
unqlite是一个标准的键/值存储数据库,类似于BerkeleyDB,Tokyo Cabinet,LevelDB等,但具有丰富的功能集,包括对事务的支持(ACID)。在KV存储下,键和值都被视为简单的字节数组,因此内容可以是ASCII字符串,二进制blob甚至磁盘文件。
简单概括,unqlite是一个标准C语言实现的轻量级、高性能nosql数据库,经过编译后,它只有几百KB大小,可以嵌入到我们的App中,它与SQLite数据库类似,区别在于一个是基于SQL的关系型数据库,一个是NoSql数据库。
unqlite_flutter
既然unqlite是一个C语言编写的数据库,那么Flutter开发如何使用它呢?
随着Dart版本的不断迭代,Dart语言的FFI接口逐渐成熟,FFI类似于Java的JNI,赋予了Dart语言直接调用C语言的能力。有了这种能力,那么基于C/C++开发的优秀的高性能的库,都可以用于Flutter开发中,可以说是C/C++生态为我所用!想学习Flutter的FFI开发,可以参考博主的B站视频 Dart FFI开发入门 以及 程序员的C
当然,这里我已经完成了Dart FFI调用的封装,可以直接依赖我开发好的插件库——unqlite_flutter
目前已完成的功能:
key-value 储存
JSON 文档储存
快速上手
简单键值对存储
添加依赖:
unqlite: ^0.0.3
unqlite_flutter: ^0.0.3
示例代码:
// 创建或打开一个数据库
UnQLite db = UnQLite.open("${appDocDir.path}/test.db");
// 保存键值对
db.store("name", "Alex");
db.store("age", 18);
db.store(19, "haha");
// 通过指定泛型获取值
debugPrint(db.fetch<String>("name"));
debugPrint('${db.fetch<int>("age")}');
debugPrint(db.fetch<String>(19));
// 另一种获取值的方法
db.fetchCallback<int>("age", (val) {
debugPrint('age=$val');
});
当然,还有另一种获取数据的方式,可能比fetch
更快:
var cursor = db.cursor();
cursor.seek('name');
debugPrint('=> ${cursor.key} => ${cursor.value}');
你还可以使用事务。在一个事务中,如果发生异常,你可以回滚所有操作:
var trans = db.transaction().begin();
try {
for (var i = 0; i < 100000; i++) {
if (i == 10) {
// 这里我们抛出一个异常
throw Exception('test');
}
db.store("transaction_$i", "here is a transaction_$i");
}
trans.commit();
} catch (e) {
// 处理异常,手动回滚事务
trans.rollback();
}
以上示例中,我们保存十万个数据,一旦中间发生了什么异常,我们执行回滚操作,则前面所有的保存的数据将被取消。
你还可以使用迭代器来进行遍历:
for (var entry in db.cursor()) {
var content = '${entry.key} => ${entry.value}';
debugPrint(content);
}
JSON
处理JSON文档
UnQLite db = UnQLite.open("${appDocDir.path}/test2.db");
var users = db.collection("users");
// Create a collection
users.create();
// 保存JSON
users.store(jsonDecode('''
{
"title": "test json string",
"author": [
"arcticfox1919"
],
"year": 2022,
"like": "flutter"
}
'''));
users.store({'name': 'Mickey', 'age': 17});
users.store([
{'name': 'Alice', 'age': 18},
{'name': 'Bruce', 'age': 19},
{'name': 'Charlie', 'age': 20},
]);
// 获取全部数据
print(users.all());
// print(users.fetch(0));
// print(users.fetch(1));
// print(users.fetch(2));
// print(users.errorLog());
print(users.creationDate());
print(users.len());
print(users.fetchCurrent());
// 删除全部
users.drop();
db.close();
为什么你应该使用unqlite_flutter?
比 Hive 更快,占用更少的内存
可以支持 JSON 文档
它有什么缺点? 因为使用了dart ffi,所以不能支持Flutter web。
下面是一些性能测试数据,这里没有列出内存占用的百分比,但可以肯定 unqlite 比Hive 使用了更少的内存:
UnQLite:
UnQLite init:1 ms
write 100,000 entries :611 ms
fetch 100,000 entries :370 ms
seek 100,000 entries :215 ms
iterate 100,000 entries :225 ms
transaction rollback :39 ms
Hive:
Hive init:48 ms
put 100,000 entries :807 ms
get 100,000 entries :290 ms
这是用于测试的代码,两者都在同一部真机上以profile模式运行:
testUnQLite() async {
var appDocDir = await getApplicationDocumentsDirectory();
final start = DateTime.now().millisecondsSinceEpoch;
UnQLite db = UnQLite.open("${appDocDir.path}/test.db");
final t1 = DateTime.now().millisecondsSinceEpoch;
for (var i = 0; i < 100000; i++) {
db.store("my_key_$i", "Here is a value for testing—$i");
}
final t2 = DateTime.now().millisecondsSinceEpoch;
for (var i = 0; i < 100000; i++) {
var r = db.fetch<String>("my_key_$i");
// debugPrint("fetch :$r");
}
final t3 = DateTime.now().millisecondsSinceEpoch;
var cursor = db.cursor();
for (var i = 0; i < 100000; i++) {
cursor.seek('my_key_$i');
// debugPrint('=> ${cursor.key} => ${cursor.value}');
}
final t4 = DateTime.now().millisecondsSinceEpoch;
var count = 0;
for (var entry in db.cursor()) {
count++;
var content = '${entry.key} => ${entry.value}';
// debugPrint(content);
}
print('count => $count');
final t5 = DateTime.now().millisecondsSinceEpoch;
var trans = db.transaction().begin();
try {
for (var i = 0; i < 100000; i++) {
if (i == 10) {
throw Exception('test');
}
db.store("transaction_$i", "here is a transaction_$i");
}
trans.commit();
} catch (e) {
trans.rollback();
}
final t6 = DateTime.now().millisecondsSinceEpoch;
debugPrint("UnQLite init:${t1-start} ms");
debugPrint("write 100,000 entries :${t2-t1} ms");
debugPrint("fetch 100,000 entries :${t3-t2} ms");
debugPrint("seek 100,000 entries :${t4-t3} ms");
debugPrint("iterate 100,000 entries :${t5-t4} ms");
debugPrint("transaction rollback :${t6-t5} ms");
db.close();
}
testHive() async {
var appDocDir = await getApplicationDocumentsDirectory();
var path = appDocDir.path;
final start = DateTime.now().millisecondsSinceEpoch;
Hive.init(path);
var box = await Hive.openBox('testBox');
final t1 = DateTime.now().millisecondsSinceEpoch;
for (var i = 0; i < 100000; i++) {
box.put("my_key_$i", "here is a transaction_$i");
}
final t2 = DateTime.now().millisecondsSinceEpoch;
for (var i = 0; i < 100000; i++) {
var name = box.get('my_key_$i');
}
final t3 = DateTime.now().millisecondsSinceEpoch;
box.close();
debugPrint("Hive init:${t1-start} ms");
debugPrint("put 100,000 entries :${t2-t1} ms");
debugPrint("get 100,000 entries :${t3-t2} ms");
}
来源:https://juejin.cn/post/7129165901693190151


猜你喜欢
- Java处理JSON数据有三个比较流行的类库FastJSON、Gson和Jackson。JacksonJackson是由其社区进行维护,简单
- @ModelAttribute在父类、子类的执行顺序被 @ModelAttribute 注解的方法会在Controller每个方法执行之前都
- 这个是由于快捷键冲突造成的:所以可以查应用比如:1)搜狗输入法中设置的语句2)QQ音乐的快捷键3)有道词典的快键键把上面找的快键键删除,那么
- Commons Beanutils是Apache开源组
- 方法一class Program { [STAThread] static
- 这篇文章主要介绍了SpringBoot多模块项目框架搭建过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- Java中Filter、Servlet、Listener的学习资料,希望大家喜欢1、Filter的功能filter功能,它使用户可以改变一个
- 在这篇文章中,我精选了几个比较适合 Java 编码的 IDEA 主题供小伙伴们选择。另外,我自己用的是 One Dark theme 这款。
- 1 修改项目打包类型在pom.xml里,项目打包类型将jar设置成war:<packaging>war</packagin
- 一、java多线程基本入门java多线程编程还是比较重要的,在实际业务开发中经常要遇到这个问题。 java多线程,传统创建线程的方式有两种。
- 本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下XML布局主页面main布局<com.handma
- 附GitHub源码:WebViewExplore一、WebView的基础配置WebSettings ws = getSettings();w
- 从一个Stream中过滤null值复习一个Stream 包含 null 数据的例子.Java8Examples.javapackage co
- 一、需求C# 项目生成 dll,在反编译工具下,好比皇帝的新装,dll 内部的代码看的一清二楚,在这里推荐一个工具ConfuserEx,可以
- public static class EncryptAndDecrypt { &
- 本文实例为大家分享了C语言实现两个矩阵相乘的具体代码,供大家参考,具体内容如下程序功能:实现两个矩阵相乘的C语言程序,并将其输出代码如下:#
- 普通Java-Kotlin类添加注释添加类时注释作者信息和日期时间依次打开File—>Settings—>editor—>
- 快捷键辅助类 class HotKey { /// <summary> /// 如果函数执行成功,返回值不为0。 /// 如果函
- Flink中设计了用户自定义函数体系(User Defined Function,UDF),开发人员实现业务逻辑就是开发UDF。一、环境对象
- 程序结构:一、配置 1. 在pom.xml中添加依赖pom.xml文件如下:<?xml version="1.0&