尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

FlatBuffers 在 Dart 中的完整使用指南:从 flatc 代码生成到 Object API 实战

FlatBuffers 在 Dart 中的完整使用指南:从 flatc 代码生成到 Object API 实战 FlatBuffers 在 Dart 中的完整使用指南从 flatc 代码生成到 Object API 实战【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers本篇技术指南以 FlatBuffers 官方文档中 Dart 语言专项章节docs/source/languages/dart.md为核心结合仓库内 Dart 运行时源码与示例代码系统讲解如何在 Dart/Flutter 项目中使用 FlatBuffers 完成高效的二进制序列化。读完本文你将掌握通过flatc的--dart选项生成 Dart 代码、读取与构建 FlatBuffer、理解 Dart 实现与 Dart SDK 内置实现的差异以及使用--gen-object-api生成更易用的 Object API 进行对象级读写。前置准备先掌握 FlatBuffers 通用基础在使用 Dart 语言特性之前需要先掌握 FlatBuffers 的通用工作流。官方建议按以下顺序阅读文档教程Tutorial包含所有受支持语言含 Dart的完整 FlatBuffers 通用用法指南是理解本文的基础构建 flatcBuilding了解如何编译出flatc命令行编译器schema 编译器使用flatc熟悉flatc的各种生成选项schema 编写Writing a schema掌握 FlatBuffers IDL 的语法。本页文档dart.md的定位是在通用指南之上专门覆盖FlatBuffers 在 Dart 中的独有细节与差异。FlatBuffers Dart 库的代码位置Dart 运行时库代码位于仓库的dart/目录核心文件包括dart/lib/flat_buffers.dart运行时主库包含Builder构建器、BufferContext缓冲上下文、Reader系列标量/字符串/列表读取器等核心类dart/lib/flex_buffers.dartFlexBuffers 的 Dart 实现dart/lib/src/其他辅助源码dart/pubspec.yaml包名flat_buffers版本 25.12.19要求 Dart SDK2.17.0 4.0.0并依赖test、path、lints等开发依赖。在pubspec.yaml的描述中可以看到该实现基于 Dart SDK 团队 Konstantin Scheglov 与 Paul Berry 的原始工作这正是下文与 Dart SDK 前端 flat_buffers 的差异一节的由来。测试 Dart 库DartTest.sh 一键验证Dart 库的测试代码位于tests/目录官方文档提到的测试入口是dart_test.dart但在当前仓库中测试文件已组织在 dart/test/ 下如flat_buffers_test.dart、flex_builder_test.dart、flex_reader_test.dart、flex_types_test.dart等并配有多份*_generated.dart生成代码与monsterdata_test.mon二进制测试数据。运行测试使用 tests/DartTest.sh 脚本在 Windows 上可参考DartTest.bat。从脚本内容可以看到完整测试流程# 检查 Dart SDK 是否安装 command -v dart /dev/null 21 || { echo 2 Dart tests require dart to be in path but its not installed. Aborting. exit 1 } # 用 flatc 生成测试所需的 Dart 代码--dart 与 --gen-object-api 同时启用 ../flatc --dart --gen-object-api -I include_test -o ../dart/test monster_test.fbs ../flatc --dart --gen-object-api -I include_test/sub -o ../dart/test include_test/include_test1.fbs ../flatc --dart --gen-object-api -I include_test -o ../dart/test include_test/sub/include_test2.fbs # 复制测试二进制数据与 schema cp monsterdata_test.mon ../dart/test cp monster_test.fbs ../dart/test cd ../dart ../flatc --dart --gen-object-api -o ./test ./test/enums.fbs ../flatc --dart --gen-object-api -o ./test ./test/bool_structs.fbs # 更新依赖并执行测试 dart pub get dart test注意脚本要求系统已安装 Dart SDK 并将dart命令加入 PATH同时仓库根目录需存在可执行的flatc。测试脚本中使用的--dart --gen-object-api组合正是本文后面要重点讲解的两个关键选项。在 Dart 中使用 FlatBuffers 库基本流程flatc 生成 运行时读取FlatBuffers 在 Dart 中同时支持**读取reading和写入writing**二进制 FlatBuffer。使用步骤分为两步用flatc的--dart选项从 schema 生成 Dart 类例如flatc --dart monster.fbs在代码中同时引入运行时库与生成代码即可读写 FlatBuffer。读取 FlatBuffer 二进制文件文档给出了读取 FlatBuffer 二进制文件的完整示例先将二进制文件读入Listint再传给生成类如Monster的工厂构造函数import dart:io as io; import package:flat_buffers/flat_buffers.dart as fb; import ./monster_my_game.sample_generated.dart as myGame; Listint data await new io.File(monster.dat).readAsBytes(); var monster new myGame.Monster(data);随后即可像访问普通 Dart 对象一样读取字段值var hp monster.hp; var pos monster.pos;对照仓库中的生成代码 dart/example/monster_my_game.sample_generated.dart可以看到Monster的工厂构造函数内部通过fb.BufferContext.fromBytes(bytes)创建只读的缓冲上下文再由reader.read(rootRef, 0)定位根对象factory Monster(Listint bytes) { final rootRef fb.BufferContext.fromBytes(bytes); return reader.read(rootRef, 0); }在运行时库 dart/lib/flat_buffers.dart 中BufferContext.fromBytes会把Listint包装成ByteData视图若传入的是Uint8List则直接复用其底层 buffer零拷贝。字段的读取依赖Reader.vTableGet通过 VTable 查找字段偏移若字段不存在则返回默认值这正是 FlatBuffers字段可缺省、向后兼容的核心机制。写入 FlatBufferBuilder 的两种风格文档提到本实现的代码生成提供两类构建类ObjectBuilder与Builder 类。仓库示例 dart/example/example.dart 中两种方式均有完整演示方式一底层 Builder贴近其他语言的 builders内存更省final builder fb.Builder(initialSize: 1024); final int? weaponOneName builder.writeString(Sword); // ... 依次写入字符串、列表、struct、table final int monsteroff monster.finish(); builder.finish(monsteroff); if (verify(builder.buffer)) { print(The FlatBuffer was successfully created with a builder and verified!); }这种方式要求按预先顺序pre-order构造所有数据即先写入嵌套的子对象字符串、vector、struct最后再构建引用它们的 table。从MonsterBuilder的生成代码可以看出begin()调用fbBuilder.startTable(10)各add*方法调用addInt16、addOffset、addStruct等底层方法finish()调用fbBuilder.endTable()结束 table 并触发 VTable 去重。Builder构造参数支持initialSize初始缓冲字节数默认 1024、internStrings字符串驻留池、deduplicateTablesVTable 去重默认开启与自定义Allocator。方式二ObjectBuilder更易用代价是分配更多引用var monsterBuilder my_game.MonsterObjectBuilder( pos: my_game.Vec3ObjectBuilder(x: 1.0, y: 2.0, z: 3.0), mana: 150, hp: 300, name: Orc, inventory: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], color: my_game.Color.Red, weapons: [ my_game.WeaponObjectBuilder(name: Sword, damage: 3), axe, ], equippedType: my_game.EquipmentTypeId.Weapon, equipped: axe, ); var buffer monsterBuilder.toBytes(); // 直接得到序列化后的 Uint8ListObjectBuilder的toBytes()内部会新建Builder并完成finish一次性返回可直接落盘或网络传输的字节流。运行时库中ObjectBuilder抽象类还提供了getOrCreateOffset允许复用同一 Builder 实例中已写入的偏移量。两种风格对应运行时的两套核心类写入侧Builder负责从缓冲区尾部向头部反向写入addField/_prepare处理对齐endTable计算并去重 VTable见 dart/lib/flat_buffers.dart 中_VTable类的_offsetsMatch逻辑结构相同的表会共享同一 VTable读取侧BufferContext提供各标量类型的_get*系列方法全部按小端序Endian.little读取Reader及其子类Int32Reader、StringReader、ListReader等负责类型化取值列表读取器默认惰性读取lazy仅在访问元素时才解析进一步降低反序列化开销。与 Dart SDK 前端 flat_buffers 的关键差异本仓库的实现大量借鉴了 Dart SDK front end/analyzer 包内部使用的实现但做了若干显著改动官方文档列出了五点理解这些差异对于从 Dart SDK 迁移到本库尤为重要移除了布尔列表的打包packed支持。该特性在其他语言实现中并不标准、互不兼容。与 JavaScript 实现类似布尔列表中的null 值会被当作 false 处理。当然仍然可以在单个标量字段内自行打包位数据但这需要在应用侧手工完成。枚举改用专门的枚举类。Dart SDK 实现使用普通 Dart 枚举这仅在枚举总是从 1 开始索引时才正确而 FlatBuffers 并不要求这一点。本实现采用类似枚举的专用类见EquipmentTypeId、Color的生成代码每个常量包装一个value并附带fromValue工厂与values映射表确保 FlatBuffers 与 Dart 及其他平台之间的映射正确。完整支持 struct 与 struct 向量。SDK 实现似乎不支持 FlatBuffer struct 或 struct 向量把所有东西都当作内建标量或 table本实现以与其他非 Dart 实现兼容的方式处理 struct并正确处理 struct 向量为此改造了许多以low前缀命名的方法。int64/uint64 不做浮点降级并新增 16 位整数支持。SDK 实现把 int64/uint64 当作 float64 处理本实现不会如此这可能在 JavaScript 兼容性上带来问题——但可以通过直接使用 JavaScript 实现、或定制一个把所有 64 位数字当作浮点数的实现来规避。支持 Dart VM 与 Flutter 是本实现更重要的目标。这也解释了为何运行时库中Uint64Reader带有WARNING: May have compatibility issues with JavaScript的注释见 dart/lib/flat_buffers.dart。代码生成同时提供 ObjectBuilder 与 Builder 两类 API。ObjectBuilder 生成的代码与 SDK 中消费 FlatBuffers 的类非常相似更易用代价是额外分配更多对象引用Builder 类则产出更接近其他语言 builder 风格的代码内存效率更高。文本解析JSON/Schema的限制当前 Dart 实现尚不支持直接从 Dart 解析文本格式包括 Schema 与 JSON。如果需要文本解析能力可以通过Dart Native Extensions 调用 C 解析器实现——可参考仓库中 src/idl_parser.cppflatc的 C 解析核心与 src/idl_gen_text.cpp文本输出等 C 侧实现。需要说明的是该方案目前不适用于 Flutter受相关 Flutter 平台问题限制详情可关注官方 issue 跟踪进展。对于纯 Dart/Flutter 场景建议直接消费二进制格式或在服务端/构建期完成 JSON 到二进制的转换。基于对象的 APIObject based API--gen-object-apiFlatBuffers 的立身之本就是内存效率因此其基础 API 围绕尽量少分配设计——这导致 API 使用上较笨拙要求预顺序构造所有数据且变更mutation困难。当效率不是首要考量时可以通过--gen-object-api选项生成更便捷的基于对象的 API它能把 FlatBuffer解包unpack成普通对象与列表从而支持便捷的构造、访问与变更变更后再**打包pack**回新的 FlatBuffer。这一点也在 tests/DartTest.sh 中得到印证——测试生成代码时总是同时传入--dart --gen-object-api。典型使用方式unpack → 修改 → pack文档给出了完整的三步用法// Deserialize from buffer into object. MonsterT monster Monster(flatbuffer).unpack(); // Update object directly like a Dart class instance. print(monster.Name); monster.Name Bob; // Change the name. // Serialize into new flatbuffer. final fbb Builder(); fbb.Finish(monster.pack(fbb));以MonsterTT 后缀即 Object API 生成的普通 Dart 类为例unpack()将 FlatBuffer 展开为可直接读写的 Dart 对象对属性直接赋值即完成修改pack(fbb)则把对象重新序列化进 Builder最后由fbb.Finish(...)产出新的二进制缓冲。整个流程让读-改-写变成普通 Dart 对象操作同时生成的二进制仍与其他语言实现完全互通。底层机制对应解包方向生成类内部基于运行时库的Reader/BufferContext见 dart/lib/flat_buffers.dart逐个字段读取把 table/struct/vector 全部物化为 Dart 对象与List打包方向pack利用Builder的writeString、writeList*、addOffset等 API 把对象图写回缓冲区其中writeString支持asciiOptimization纯 ASCII 字符串直接拷贝避免utf8.encode的转换开销见源码注释并支持通过internStrings做字符串驻留去重。与官方教程的衔接本文聚焦 Dart 特有的细节如需更深入、完整的端到端示例schema 编写 → flatc 生成 → 各语言读写请参阅 docs/source/tutorial.md。仓库中 dart/example/example.dart 提供了可直接运行的完整读写示例dart/README.md 则说明了包的发布信息与 flatc 版本对应关系建议下载与你所用 Dart 包版本匹配的 flatc。Dart 运行时生成的代码与 C/Java/Go 等其他语言实现完全二进制互通这也是 FlatBuffers 跨平台序列化的核心价值所在。总结在 Dart 中使用 FlatBuffers 的要点可归纳为用flatc --dart可选叠加--gen-object-api从 schema 生成代码引入package:flat_buffers/flat_buffers.dart运行时库读取侧使用生成类的工厂构造函数 VTable 驱动的惰性Reader高效且零分配访问字段写入侧按先子后父的顺序用Builder构建或使用更便捷的 ObjectBuilder/Object API牢记与 Dart SDK 内置实现的五点差异布尔列表打包、枚举类、struct 支持、64 位整数、双 API 风格避免迁移踩坑文本解析在纯 Dart 中暂不支持需借助 C 解析器或提前转换。这些能力使 Dart 与 Flutter 应用能够在移动端、桌面端与 Web 场景下与其他语言无缝交换高效紧凑的二进制数据。【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表