Flutter的鍵值存儲(chǔ)數(shù)據(jù)庫使用示例詳解
Flutter 鍵值存儲(chǔ)數(shù)據(jù)庫
鍵值存儲(chǔ)是開發(fā)中十分常見的需求,在Flutter開發(fā)中,一般使用 shared_preferences 插件來實(shí)現(xiàn)。shared_preferences 本質(zhì)上就是將鍵值對(duì)保存到一個(gè)XML文件中進(jìn)行持久化。
而shared_preferences 實(shí)際上存在一定缺陷,譬如其性能較差,不適合處理大量數(shù)據(jù),不能創(chuàng)建新的XML文件,所有數(shù)據(jù)存在同一個(gè)文件中。除此外,還有其他一些持久化方案,如SQLite、Hive等。
SQLite是關(guān)系型數(shù)據(jù)庫,使用起來相對(duì)繁瑣;而Hive是用Dart實(shí)現(xiàn)的一個(gè)輕量級(jí)鍵值對(duì)數(shù)據(jù)庫,它使用簡(jiǎn)單,但同樣性能較差,而且在儲(chǔ)存大量數(shù)據(jù)時(shí),更加耗費(fèi)內(nèi)存,因?yàn)樗且淮涡詫⑺袛?shù)據(jù)讀取到內(nèi)存中,這在移動(dòng)端不是很可取。當(dāng)然,如果你只是用來存儲(chǔ)幾個(gè)簡(jiǎn)單的配置項(xiàng)數(shù)據(jù),那也夠用了。
unqlite
以上概述了一下Flutter的鍵值持久化方案,那么接下來就隆介紹一下本文推薦的Flutter鍵值存儲(chǔ)方案——一個(gè)輕量級(jí)嵌入式nosql數(shù)據(jù)庫unqlite!
unqlite是一個(gè)嵌入式的數(shù)據(jù)庫,它實(shí)現(xiàn)了一個(gè)獨(dú)立的、無服務(wù)器、零配置、事務(wù)性的nosql數(shù)據(jù)庫引擎。它是一個(gè)文檔存儲(chǔ)數(shù)據(jù)庫,類似于MongoDB, Redis, CouchDB等,同時(shí)也是一個(gè)標(biāo)準(zhǔn)的key/value存儲(chǔ)數(shù)據(jù)庫,類似于BerkeleyDB, LevelDB, 等。
- 首先unqlite是一個(gè)無服務(wù)器的數(shù)據(jù)庫,這意味著它的通訊是直接讀取數(shù)據(jù)庫文件的(從磁盤讀?。?,沒有中間服務(wù)器進(jìn)程。
- 然后unqlite也不像其他服務(wù)器一樣,需要安裝、配置以及權(quán)限管理等。它是直接嵌入到我們的程序中的
- unqlite是一個(gè)單一數(shù)據(jù)庫文件,它的數(shù)據(jù)庫是一個(gè)普通的磁盤文件。這就意味著你可以把它隨意的復(fù)制或備份到其他地方。
- unqlite文件格式是跨平臺(tái)的。在一臺(tái)機(jī)器上編寫的數(shù)據(jù)庫文件可以復(fù)制到具有不同體系結(jié)構(gòu)的不同機(jī)器上并在其上使用。
- unqlite是一個(gè)標(biāo)準(zhǔn)的鍵/值存儲(chǔ)數(shù)據(jù)庫,類似于BerkeleyDB,Tokyo Cabinet,LevelDB等,但具有豐富的功能集,包括對(duì)事務(wù)的支持(ACID)。在KV存儲(chǔ)下,鍵和值都被視為簡(jiǎn)單的字節(jié)數(shù)組,因此內(nèi)容可以是ASCII字符串,二進(jìn)制blob甚至磁盤文件。
簡(jiǎn)單概括,unqlite是一個(gè)標(biāo)準(zhǔn)C語言實(shí)現(xiàn)的輕量級(jí)、高性能nosql數(shù)據(jù)庫,經(jīng)過編譯后,它只有幾百KB大小,可以嵌入到我們的App中,它與SQLite數(shù)據(jù)庫類似,區(qū)別在于一個(gè)是基于SQL的關(guān)系型數(shù)據(jù)庫,一個(gè)是NoSql數(shù)據(jù)庫。
unqlite_flutter
既然unqlite是一個(gè)C語言編寫的數(shù)據(jù)庫,那么Flutter開發(fā)如何使用它呢?
隨著Dart版本的不斷迭代,Dart語言的FFI接口逐漸成熟,F(xiàn)FI類似于Java的JNI,賦予了Dart語言直接調(diào)用C語言的能力。有了這種能力,那么基于C/C++開發(fā)的優(yōu)秀的高性能的庫,都可以用于Flutter開發(fā)中,可以說是C/C++生態(tài)為我所用!想學(xué)習(xí)Flutter的FFI開發(fā),可以參考博主的B站視頻 Dart FFI開發(fā)入門 以及 程序員的C
當(dāng)然,這里我已經(jīng)完成了Dart FFI調(diào)用的封裝,可以直接依賴我開發(fā)好的插件庫——unqlite_flutter
目前已完成的功能:
- key-value 儲(chǔ)存
- JSON 文檔儲(chǔ)存
快速上手
簡(jiǎn)單鍵值對(duì)存儲(chǔ)
添加依賴:
unqlite: ^0.0.3 unqlite_flutter: ^0.0.3
示例代碼:
// 創(chuàng)建或打開一個(gè)數(shù)據(jù)庫
UnQLite db = UnQLite.open("${appDocDir.path}/test.db");
// 保存鍵值對(duì)
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');
});
當(dāng)然,還有另一種獲取數(shù)據(jù)的方式,可能比fetch更快:
var cursor = db.cursor();
cursor.seek('name');
debugPrint('=> ${cursor.key} => ${cursor.value}');
你還可以使用事務(wù)。在一個(gè)事務(wù)中,如果發(fā)生異常,你可以回滾所有操作:
var trans = db.transaction().begin();
try {
for (var i = 0; i < 100000; i++) {
if (i == 10) {
// 這里我們拋出一個(gè)異常
throw Exception('test');
}
db.store("transaction_$i", "here is a transaction_$i");
}
trans.commit();
} catch (e) {
// 處理異常,手動(dòng)回滾事務(wù)
trans.rollback();
}
以上示例中,我們保存十萬個(gè)數(shù)據(jù),一旦中間發(fā)生了什么異常,我們執(zhí)行回滾操作,則前面所有的保存的數(shù)據(jù)將被取消。
你還可以使用迭代器來進(jìn)行遍歷:
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},
]);
// 獲取全部數(shù)據(jù)
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();
為什么你應(yīng)該使用unqlite_flutter?
- 比 Hive 更快,占用更少的內(nèi)存
- 可以支持 JSON 文檔
它有什么缺點(diǎn)? 因?yàn)槭褂昧薲art ffi,所以不能支持Flutter web。
下面是一些性能測(cè)試數(shù)據(jù),這里沒有列出內(nèi)存占用的百分比,但可以肯定 unqlite 比Hive 使用了更少的內(nèi)存:
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
這是用于測(cè)試的代碼,兩者都在同一部真機(jī)上以profile模式運(yùn)行:
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的鍵值存儲(chǔ)數(shù)據(jù)庫使用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Flutter鍵值存儲(chǔ)數(shù)據(jù)庫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
簡(jiǎn)單談?wù)刟ndroid studio 的單元測(cè)試
昨天在完善項(xiàng)目的時(shí)候,需要進(jìn)行單元測(cè)試,在Eclipse環(huán)境中進(jìn)行是很簡(jiǎn)單的,但是在Android Studio環(huán)境中進(jìn)行單元測(cè)試,在國內(nèi)找了很多資料,大都是人云亦云,本文發(fā)布出來供大家學(xué)習(xí)參考。2016-08-08
Android 中LayoutInflater.inflate()方法的介紹
這篇文章主要介紹了Android 中LayoutInflater.inflate()方法的介紹的相關(guān)資料,希望通過本文大家能掌握這部分內(nèi)容,需要的朋友可以參考下2017-09-09
Android 斷點(diǎn)下載和自動(dòng)安裝的示例代碼
本篇文章主要介紹了Android斷點(diǎn)下載和自動(dòng)安裝的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01
Android中代碼運(yùn)行指定Apk的簡(jiǎn)單方法
這篇文章主要介紹了Android中代碼運(yùn)行指定Apk的簡(jiǎn)單方法,有需要的朋友可以參考一下2014-01-01
Android Studio獲取配置資源與第三方包信息的方法
在 Android 開發(fā)中,我們經(jīng)常需要從資源文件中獲取顏色、字符串、數(shù)值等配置信息,以及獲取應(yīng)用的包信息和第三方依賴信息,下面詳細(xì)介紹這些操作的方法,需要的朋友可以參考下2025-04-04
kotlin中的模塊化結(jié)構(gòu)組件及工作原理
本文介紹了Kotlin中模塊化結(jié)構(gòu)組件,包括ViewModel、LiveData、Room和Navigation的工作原理和基礎(chǔ)使用,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2025-03-03
Android實(shí)現(xiàn)四級(jí)聯(lián)動(dòng)地址選擇器
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)四級(jí)聯(lián)動(dòng)地址選擇器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-10-10
解決Android popupWindow設(shè)置背景透明度無效的問題
這篇文章主要介紹了解決Android popupWindow設(shè)置背景透明度無效的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08
Android webveiw 出現(xiàn)棧錯(cuò)誤解決辦法
這篇文章主要介紹了Android webveiw 出現(xiàn)棧錯(cuò)誤解決辦法的相關(guān)資料,出現(xiàn)java.lang.UnsupportedOperationException: For security reasons, WebView is not allowed in privileged processes,這里提供解決辦法,需要的朋友可以參考下2017-08-08

