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");
  }

以上就是Flutter的键值存储数据库使用示例详解的详细内容,更多关于Flutter键值存储数据库的资料请关注Devmax其它相关文章!

Flutter的键值存储数据库使用示例详解的更多相关文章

  1. 详解前端HTML5几种存储方式的总结

    本篇文章主要介绍了前端HTML5几种存储方式的总结 ,主要包括本地存储localstorage,本地存储sessionstorage,离线缓存(application cache),Web SQL,IndexedDB。有兴趣的可以了解一下。

  2. PhoneGap / iOS上的SQLite数据库 – 超过5mb可能

    我误解了什么吗?Phonegap中的sqlitedbs真的有5mb的限制吗?我正在使用Phonegap1.2和iOS5.解决方法您可以使用带有phonegap插件的原生sqliteDB,您将没有任何限制.在iOS5.1中,Websql被认为是可以随时删除的临时数据…

  3. ios – 领域:如何获取数据库的当前大小

    是否有RealmAPI方法使用RealmSwift作为数据存储来获取我的RealmSwift应用程序的当前数据库大小?

  4. ios – Realm – 无法使用现有主键值创建对象

    我有一个对象有许多狗的人.应用程序有单独的页面,它只显示狗和其他页面显示人的狗我的模型如下我有人存储在Realm中.人有详细页面,我们取,并显示他的狗.如果狗已经存在,我会更新该狗的最新信息并将其添加到人的狗列表中,否则创建新狗,保存并将其添加到人员列表中.这适用于coredata.在尝试用他的狗更新人时,领域会抛出异常无法使用现有主键值创建对象解决方法这里的问题是,即使你正在创建一个全新的Rea

  5. ios – UIWebView中的WebSQL / SQLite数据库的最大大小(phonegap)

    我知道一般来说,Web应用程序的本地存储空间有5MB的限制.本地网页浏览应用程式是否也有这个限制?

  6. ios – Firebase离线存储高级 – 手动同步和进度信息

    >我可以提供一个捆绑数据库–安装App后我可以已经离线查询了Firebase数据?然后我有另一个关于Firebase的主要问题:>JSON存储是伟大的–但是这样我们不关心一个独特的结构,我们必须注意这一点插入总是正确的数据集?我从来没有试图显示实际的进展,但是当您从firebase中检索数据时,始终会在成功检索数据时调用onDataChange方法.https://firebase.google.com/docs/database/android/retrieve-data#read_data_onceC

  7. ios – 如何处理多用户数据库

    我的应用程序就像很多应用程序–它有一个用户输入用户名和密码的登录屏幕,以及登录按钮我的应用程序还使用CoreData来保存大多数用户的业务对象,当然也是用户特定的.我也有一个登出按钮来启用切换用户.这不会发生很多,但仍然是必要的).现在如果不同的用户登录,我需要获取他的具体数据.但是我该如何做呢?

  8. ios – Swift从Firebase数据库中获取特定价值

    我正在尝试从Firebase数据库中获取特定值.我看了一些像谷歌这样的文件,但我做不到.这是数据库的JSON文件:SWIFT代码:我想获得用户的电子邮件价值,而不是每个人.我怎样才能做到这一点?解决方法在您的代码中,快照将包含子值的字典.要访问它们,请将snapshot.value转换为Dictionary,然后访问各个子项是一个快照

  9. ios – Realm Swift:在卸载应用程序后是否可以保留数据库?

    使用realmswift,即使从设备上卸载应用程序,是否可以在设备内存中保留和维护应用程序的领域数据库文件?非常感谢您的帮助.解决方法删除应用程序时,应用程序的所有文件都是剩余的.iOS应用程序是沙盒.这意味着每个应用程序在磁盘中都有自己的空间,并有自己的目录,这些目录充当应用程序及其数据的主页.从iPhone删除应用程序会删除此沙箱,删除与该应用程序关联的所有数据.

  10. ios – 在没有XML的情况下更新sqlite数据库

    我的应用程序需要来自sqlite数据库的数据.它将附带此数据库的一个版本,但我需要定期更新它(很可能每月一次).通常情况下,我一直在通过我设置的一堆网络服务将我的应用程序的其他部分的更新作为XML发送,但我现在正在处理的这个特定数据库非常大(大约20-30MB),而且我当我尝试以这种方式发送时出现超时错误.我尝试将数据库放在我的公司服务器上,然后将其下载到NSData对象中.然后我将该数据对象保存

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部