Hive — 一个适用于 Flutter 的轻量级 NoSQL 存储,无需原生代码即可运行。与 SQLite 或 Firebase 不同,Hive 不需要连接原生库,完全通过 Dart 运行。据 Pub.dev,2024 数据显示,Hive 已被下载超过 1000 万次,并且在每三个需要本地数据存储而无需服务器基础设施的 Flutter 项目中就有一个在使用它。
要点
Hive — 是一个完全用 Dart 编写且不需要原生库的 NoSQL 数据库。它由 Simon Leiter 于 2019 年创建,作为 Flutter 项目中 SQLite 的替代方案。Hive 以二进制 .hive 格式存储数据,该格式针对移动设备上的快速读写进行了优化。.hive 格式使用自定义序列化方案,其中每种数据类型都有自己的字节前缀,允许在无需提前了解方案的情况下读取文件 — 这与 Protocol Buffers 或 FlatBuffers 不同。
Hive 的主要理念是极致简单。数据库不需要初始化原生引擎,不包含 SQL 解析器,也不使用反射。所有操作都是通过 WriteBuffer 和 ReadBuffer 进行二进制序列化的直接 Dart 函数调用。
根据 Flutter Community(2023 年)的调查,Hive 是 Flutter 中数据存储使用量排名前 5 的软件包,在流行度上仅次于 shared_preferences,但在功能性和速度上超越它。
Hive 使用 Box 概念 — 类似于关系数据库中的表。每个 Box 是磁盘上的一个文件,包含一组键值对。键可以是 int 或 String,值可以是任何基本类型、列表、Map 或通过 TypeAdapter 的自定义对象。Box 之间相互隔离并独立打开。
Hive 不需要平台通道(platform channels)。这意味着它在 Android、iOS、Web、macOS、Windows 和 Linux 上无需额外配置即可相同运行。对于面向 Web 编译的项目,Hive 仍然是唯一的轻量级 NoSQL 解决方案 — SQLite 在浏览器中无法运行。同时,Hive 使用 IndexedDB 作为 Web 的后端,确保在浏览器环境中数据的持久性。
Hive 在写入时将数据序列化为二进制格式,在读取时反序列化。内部机制基于 BinaryWriter 和 BinaryReader,它们将数据打包成紧凑的字节数组。磁盘上的存储大小平均比相同数据的 JSON 表示小 2-3 倍。
打开 Box 时,Hive 将整个文件加载到 RAM 中。这提供了高速读取(微秒级),但限制了数据大小:建议每个 Box 存储不超过 50-100 MB。对于更大的数据量,请使用 LazyBox — 从磁盘延迟加载记录。
Hive 在 Dart isolate 隔离中单线程运行。写入操作通过文件锁定同步执行。对于异步访问,请使用带 await 的 Hive.openBox()。不支持直接从多个 isolate 并发访问 — 这需要单独的同步机制。
Hive 处于 SharedPreferences 和 SQLite 之间。它比 SharedPreferences 更复杂(支持自定义对象),但比 SQLite 更简单(不需要 SQL 查询)。让我们比较关键特性。
| 特性 | Hive | SharedPreferences | SQLite |
|---|---|---|---|
| 数据类型 | 任何类型(通过 TypeAdapter) | 仅基本类型 | SQL 类型 |
| 读取速度 | ~30,000 ops/s | ~5,000 ops/s | ~2,000 ops/s |
| 原生代码 | 不需要 | 需要(Android) | 需要 |
| Web 支持 | 是 | 否 | 否 |
| 复杂度 | 低 | 最小 | 中等 |
| 响应性 | WatchBox | 否 | 通过 ORM |
Hive 非常适合小数据量:应用程序设置、API 响应缓存、本地同步队列、收藏夹和浏览历史。如果数据不超过 50 MB 且不需要关系查询 — Hive 比 SQLite 更快更简单。
Hive 不支持多字段过滤、JOIN、聚合函数等查询。如果您需要像 “选择今天所有优先级高于 3 的任务” 这样的复杂查询 — 请使用带 drift 或 floor 的 SQLite。由于加载到内存中,Hive 也不适合存储超过 100 MB 的数据。
Hive 从初始化和打开 Box 开始。下面是典型场景的基本操作 — 在 Flutter 应用程序中存储任务列表。所有示例无需原生平台调用即可运行。
在使用 Hive 之前,需要在 main 函数中调用 Hive.initFlutter()。然后通过 Hive.openBox() 打开 Box — 结果将是一个准备好读写操作的 Box 实例。
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
void async main() {
await Hive.initFlutter();
final settingsBox = await Hive.openBox('settings');
runApp(MyApp());
}
Box 提供 put、get、delete 方法,并包含遍历所有记录的迭代器。键和值通过泛型进行类型化 — 默认情况下 Box<dynamic> 允许任何类型,但建议指定具体类型。
// 写入数据
final box = await Hive.openBox<String>('tasks');
await box.put('task_1', '购买商品');
// 读取
final task = box.get('task_1');
// 所有键
final allTasks = box.values.toList();
// 删除
await box.delete('task_1');
// 清空 Box
await box.clear();
WatchBox — Box 的扩展,用于通知订阅者变更。在 Flutter 中,这与 ValueListenableBuilder 集成:当 Box 中任何值发生变化时,widget 会自动重建,无需调用 setState。
final watchBox = await Hive.openBox('settings');
// 在 Widget 中
ValueListenableBuilder(
valueListenable: watchBox.listenable(),
builder: (context, box, _) {
final counter = box.get('counter') ?? 0;
return Text('计数器:$counter');
},
)
TypeAdapter — Hive 用于序列化自定义 Dart 对象的机制。适配器描述如何将对象转换为二进制格式(write)和恢复(read)。与 json_serializable 不同,TypeAdapter 不需要反射且运行更快。
适配器实现具有两个方法 TypeAdapter
// 数据模型
class Task {
final String title;
final bool isCompleted;
Task({required this.title, this.isCompleted = false});
}
// TypeAdapter
class TaskAdapter extends TypeAdapter<Task> {
@override
final int typeId = 0;
@override
Task read(BinaryReader reader) {
return Task(
title: reader.readString(),
isCompleted: reader.readBool(),
);
}
@override
void write(BinaryWriter writer, Task obj) {
writer.writeString(obj.title);
writer.writeBool(obj.isCompleted);
}
}
对于具有大量模型的项目,Hive 提供 hive_generator 和 build_runner。类上的 @HiveType 注解和字段上的 @HiveField 注解会自动生成适配器。当模型包含 10 个以上字段时,这很方便 — 手动编写 read/write 变得繁琐。
Hive 从内存而非磁盘执行读取,提供每秒高达 30,000 次操作的速度。优化建议:一次打开 Box 并在整个应用中重复使用,不要再次调用 openBox。初始化后使用 Hive.box()(同步 getter)— 返回已打开的 Box 而无需创建新实例。
Hive 可轻松与流行的 Flutter 状态管理器集成。对于 Provider,使用在初始化时从 Box 读取数据并通过 listenable 更新的 ChangeNotifierProvider。对于 Riverpod,适合订阅 WatchBox 的 StreamProvider。这种组合在每次 Hive 中数据变更时提供响应式 UI 更新,无需手动调用 setState。在典型的 Flutter 项目中,这种架构允许在屏幕之间同步状态而无需全局 singleton。
常见问题
Hive 在纯 Dart 中运行,因此可在任何 Dart 项目中使用:服务器端(Dart VM)、控制台或 AngularDart。对于 Flutter,额外连接带有存储路径初始化的 hive_flutter。
Hive 在打开 Box 时通过 encryptionKey 参数支持 AES-256 加密。密钥必须是 32 字节的字符串。加密后的 Box 在没有密钥的情况下无法读取 — 数据在文件级别受到保护。
Isar — 来自同一作者(Simon Leiter)的 Hive 继承者。Isar 更快,支持索引、关联和复杂查询。然而,对于不需要 Isar 关系型功能的简单场景以及对于最小依赖很重要的项目,Hive 仍然适用。
Hive 没有内置的迁移功能。如果 TypeAdapter 结构发生变化,旧数据将无法反序列化。解决方法:增加适配器的 typeId 并在代码中编写手动迁移,或者在写入新键之前对旧键使用 delete。
Hive 将 Box 完全加载到内存中。建议限制为每个 Box 50-100 MB。超过时,可能会出现 Box 打开延迟和 RAM 消耗增加。对于更大的数据量,请使用多个 Box 或带延迟加载的 LazyBox。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。