Flutter序列化与反序列化插件flat_buffers的使用

发布于 1周前 作者 wuwangju 来自 Flutter

Flutter序列化与反序列化插件flat_buffers的使用

简介

FlatBuffers for Dart 这个包用于读取和写入 FlatBuffers。大多数用户需要使用对应平台的 flatc - FlatBuffer 编译器二进制文件。您可以从 GitHub releases下载与您的dart包版本相匹配的flatc版本。

FlatBuffer编译器flatc读取FlatBuffers IDL模式并生成Dart代码。生成的类可以用于读取或写入与其他语言和平台兼容的二进制数据/文件,具体示例可以在example.dart中找到。

有关更多详细信息和文档,请访问官方网站阅读教程以及如何在Dart中使用FlatBuffers

示例代码

下面是一个完整的示例demo,展示了如何使用FlatBuffers创建和读取二进制缓冲区:

/*
 * Copyright 2018 Dan Field. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import 'package:flat_buffers/flat_buffers.dart' as fb;
import './monster_my_game.sample_generated.dart' as my_game;

// Example how to use FlatBuffers to create and read binary buffers.

void main() {
  builderTest();
  objectBuilderTest();
}

/// 使用Builder构建FlatBuffer
void builderTest() {
  final builder = fb.Builder(initialSize: 1024);
  final int? weaponOneName = builder.writeString("Sword");
  final int weaponOneDamage = 3;

  final int? weaponTwoName = builder.writeString("Axe");
  final int weaponTwoDamage = 5;

  // 构建Weapon对象
  final swordBuilder = my_game.WeaponBuilder(builder)
    ..begin()
    ..addNameOffset(weaponOneName)
    ..addDamage(weaponOneDamage);
  final int sword = swordBuilder.finish();

  final axeBuilder = my_game.WeaponBuilder(builder)
    ..begin()
    ..addNameOffset(weaponTwoName)
    ..addDamage(weaponTwoDamage);
  final int axe = axeBuilder.finish();

  // 序列化怪物名称 "Orc"
  final int? name = builder.writeString('Orc');

  // 创建一个列表表示Orc的库存
  final List<int> treasure = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  final inventory = builder.writeListUint8(treasure);
  final weapons = builder.writeList([sword, axe]);

  // Struct builders are very easy to reuse.
  final vec3Builder = my_game.Vec3Builder(builder);

  vec3Builder.finish(4.0, 5.0, 6.0);
  vec3Builder.finish(1.0, 2.0, 3.0);
  // 设置生命值为300,魔法值为150
  final int hp = 300;
  final int mana = 150;

  final monster = my_game.MonsterBuilder(builder)
    ..begin()
    ..addNameOffset(name)
    ..addInventoryOffset(inventory)
    ..addWeaponsOffset(weapons)
    ..addEquippedType(my_game.EquipmentTypeId.Weapon)
    ..addEquippedOffset(axe)
    ..addHp(hp)
    ..addMana(mana)
    ..addPos(vec3Builder.finish(1.0, 2.0, 3.0))
    ..addColor(my_game.Color.Red);

  final int monsteroff = monster.finish();
  builder.finish(monsteroff);
  if (verify(builder.buffer)) {
    print(
        "The FlatBuffer was successfully created with a builder and verified!");
  }
}

/// 使用ObjectBuilder构建FlatBuffer
void objectBuilderTest() {
  // Create the builder here so we can use it for both weapons and equipped
  // the actual data will only be written to the buffer once.
  var axe = my_game.WeaponObjectBuilder(name: 'Axe', damage: 5);

  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();

  // We now have a FlatBuffer we can store on disk or send over a network.

  // ** file/network code goes here :) **

  // Instead, we're going to access it right away (as if we just received it).
  if (verify(buffer)) {
    print(
        "The FlatBuffer was successfully created with an object builder and verified!");
  }
}

/// 验证FlatBuffer数据
bool verify(List<int> buffer) {
  // Get access to the root:
  var monster = my_game.Monster(buffer);

  // Get and test some scalar types from the FlatBuffer.
  assert(monster.hp == 80);
  assert(monster.mana == 150); // default
  assert(monster.name == "MyMonster");

  // Get and test a field of the FlatBuffer's `struct`.
  var pos = monster.pos!;
  assert(pos.z == 3.0);

  // Get a test an element from the `inventory` FlatBuffer's `vector`.
  var inv = monster.inventory!;
  assert(inv.length == 10);
  assert(inv[9] == 9);

  // Get and test the `weapons` FlatBuffers's `vector`.
  var expectedWeaponNames = ["Sword", "Axe"];
  var expectedWeaponDamages = [3, 5];
  var weps = monster.weapons!;
  for (int i = 0; i < weps.length; i++) {
    assert(weps[i].name == expectedWeaponNames[i]);
    assert(weps[i].damage == expectedWeaponDamages[i]);
  }

  // Get and test the `Equipment` union (`equipped` field).
  assert(monster.equippedType!.value == my_game.EquipmentTypeId.Weapon.value);
  assert(monster.equippedType == my_game.EquipmentTypeId.Weapon);

  assert(monster.equipped is my_game.Weapon);
  var equipped = monster.equipped as my_game.Weapon;
  assert(equipped.name == "Axe");
  assert(equipped.damage == 5);

  print(monster);
  return true;
}

关键点解析

  1. 初始化Builder:通过fb.Builder创建一个初始大小为1024字节的Builder实例。
  2. 构建Weapon对象:使用WeaponBuilder来构建武器对象,并添加到Builder中。
  3. 序列化怪物名称:将怪物的名称(如“Orc”)写入Builder中。
  4. 构建库存列表:创建一个整数列表表示怪物的库存,并将其写入Builder中。
  5. 设置生命值和魔法值:定义怪物的生命值和魔法值,并将其添加到Builder中。
  6. 构建Monster对象:使用MonsterBuilder来构建完整的怪物对象,并完成序列化。
  7. 验证FlatBuffer:通过verify函数检查生成的FlatBuffer是否正确无误。

这个示例展示了如何使用FlatBuffers进行序列化和反序列化操作,适用于Flutter应用程序中的高效数据传输和存储。


更多关于Flutter序列化与反序列化插件flat_buffers的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter序列化与反序列化插件flat_buffers的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter项目中,使用flat_buffers进行序列化和反序列化是一个高效的方法,尤其适用于需要高性能和低延迟的应用场景。以下是一个简单的示例,展示了如何在Flutter中使用flat_buffers进行序列化和反序列化。

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加flat_buffers的依赖(注意:目前Flutter官方仓库可能没有直接提供flat_buffers的插件,你可能需要使用一个兼容Dart的flat_buffers库,或者通过平台通道调用原生代码)。为了演示,我们假设存在一个兼容的Dart库。

dependencies:
  flutter:
    sdk: flutter
  flat_buffers: ^x.y.z  # 替换为实际版本号

2. 定义Schema

创建一个FlatBuffers的schema文件(例如monster.fbs),用于定义数据结构。

// monster.fbs
namespace MyGame.Sample;

table Monster {
  name:string;
  inventory:[int];
  color:Color = Blue;
}

enum Color {
  Red = 1
  Green = 2
  Blue = 3
}

root_type Monster;

3. 生成Dart代码

使用FlatBuffers编译器(flatc)生成Dart代码。

flatc --dart monster.fbs

这将生成一个包含序列化和反序列化逻辑的Dart文件(例如monster_generated.dart)。

4. 使用生成的代码进行序列化和反序列化

在你的Flutter项目中,使用生成的代码进行序列化和反序列化。

import 'package:flat_buffers/flat_buffers.dart';
import 'package:your_project/monster_generated.dart';  // 替换为实际路径

void main() {
  // 构建一个Monster对象
  var builder = new FlatBufferBuilder(1024);
  var name = builder.createString("Orc");
  var inventory = [3, 7];
  MyGameSampleMonster.createMonster(builder, name, inventory, MyGameSampleColor.Blue);
  var orc = builder.endObject();
  builder.finish(orc);

  // 获取序列化后的缓冲区
  Uint8List buffer = builder.dataBuffer();

  // 反序列化
  var monster = MyGameSampleMonster.getRootAsMonster(buffer);
  print("Monster name: ${monster.name()}");
  print("Monster inventory: ${monster.inventoryLength()}");
  for (int i = 0; i < monster.inventoryLength(); i++) {
    print("Inventory[$i]: ${monster.inventory(i)}");
  }
  print("Monster color: ${monster.color()}");
}

注意事项

  1. FlatBuffers Schema: 确保你的schema文件(如monster.fbs)正确无误,并且使用flatc生成了Dart代码。
  2. 依赖管理: 确保你的pubspec.yaml文件中正确添加了flat_buffers依赖,并运行flutter pub get
  3. 错误处理: 在实际应用中,添加适当的错误处理逻辑,以确保在序列化和反序列化过程中处理潜在的异常。

这个示例展示了如何在Flutter中使用flat_buffers进行基本的序列化和反序列化操作。根据你的实际需求,你可能需要调整schema文件和代码逻辑。

回到顶部