参考http://blog.csdn.net/yxz329130952/article/details/50706369
下载flatbuffers的源码,解压
homebrew下载安装cmake
cd到flatbuffers的源码文件夹
开始编译flatc
cd flatbuffers/
cmake -G "Unix Makefiles"
make
指定生成的对象为Unix makefile,以便cmake根据CMakeLists.txt文件生成对应的makefile文件。(在当前目录下可以看到生成的makefile文件,直接执行make命令即可得到flatc。
./flatc -j -o ~/workspace/testfb repos_schema.fbs
-j表示生成java文件,–o 表示将文件编译到指定目录~/workspace/testfb下,schema文件的位置放在命令的最后。
命令详细用法可见官方文档
命令详细用法可见官方文档
参见android FlatBuffers剖析
重点:扁平二进制的存储方式。
1.gradle添加依赖
compile 'com.github.davidmoten:flatbuffers-java:1.4.0.1'
2.定义schema
我们以这个数据样式(Json)为例
{
"error_no":0,
"message":"",
"result":{
"data":[
{
"datatype":1,
"itemdata":
{//共有字段45个
"sname":"\u5fae\u533b",
"packageid":"330611",
…
"tabs":[
{
"type":1,
"f":"abc"
},
…
]
}
},
…
],
"hasNextPage":true,
"dirtag":"soft"
}
}
3.编写schema如下
namespace flats;
table TabFB {
type : int;
f : string;
}
table ItemDataFB {
sname : string;
packageid : string;
...
tabs : [TabFB];
}
table DataFB {
datatype : int;
itemdata : ItemDataFB;
}
table ResultFB {
data : [DataFB];
hasNextPage : bool;
dirtag : string;
}
table ResponseFB {
error_no : int;
message : string;
result : ResultFB;
}
root_type ResponseFB;
就以上示例简单讲解一下:
命名空间
namespace决定了编译生成文件的存放位置,类似c的写法,同样,不同的schema文件可以互相引用,通过include的方式
table-表
表是在FlatBuffers中定义对象的主要方式,它由一个名称和一个字段列表组成。每个字段都有一个名称,一个类型和可选的默认值(如果省略,则默认为0/ NULL)。
每个字段都是可选的,因此,可以灵活地添加字段,而不必担心数据膨胀。此设计也是FlatBuffer的前后兼容机制。
注意:
根类型
root_type必须指定,序列化数据的根结构,解析开始的位置。
4.flatc编译后生成文件目录如下
5.将编译生成的文件导入工程即可开始编码了。
序列化示例
public static void writeResponseToFbFile(String fbfilepath, ResponseJson responseJson) {
FlatBufferBuilder builder = new FlatBufferBuilder(0);
int dataitemsize = responseJson.result.data.size();
int[] dataArr = new int[dataitemsize];
//data begin,每个Data下有多个itemdata
for (int i = 0; i < dataitemsize; i++) {
DataItemJson dataItemJson = responseJson.result.data.get(i);
ItemJson itemJson = dataItemJson.itemdata;
//itemdata begin
int sname = builder.createString(itemJson.sname);
int packageid = builder.createString(itemJson.packageid);
....
//tabs begin,多个tab
int tabnum = itemJson.tabs.size();
int[] tabsArr = new int[tabnum];
for (int j = 0; j < tabnum; j ++) {
//TabFB begin
TabsJson tabsJson = itemJson.tabs.get(j);
tabsArr[j] = TabFB.createTabFB(builder, tabsJson.type, builder.createString(tabsJson.f));
//TabFB end
}
int tabs = ItemDataFB.createTabsVector(builder,tabsArr);
//tabs end,返回tabs vtable所在位置的offset
ItemDataFB.startItemDataFB(builder);
ItemDataFB.addSname(builder,sname);
ItemDataFB.addPackageid(builder, packageid);
ItemDataFB.addTabs(builder,tabs);
int itemdataofset = ItemDataFB.endItemDataFB(builder);
//ItemDataFB end,返回itemdata的offset
//DataFb
int dataoffset = DataFB.createDataFB(builder, dataItemJson.datatype, itemdataofset);
//将当前data数据的offset写入vtable
dataArr[i] = dataoffset;
}
//封装result
int data = ResultFB.createDataVector(builder,dataArr);
int dirtag = builder.createString(responseJson.result.dirtag);
int resultOfset = ResultFB.createResultFB(builder,data,responseJson.result.hasNextPage,dirtag);
// response
int message = builder.createString(responseJson.message);
int responseOfset = ResponseFB.createResponseFB(builder,responseJson.error_no,message,resultOfset);
//调用finish结束此次构造
builder.finish(responseOfset);
}
序列化后的数据写入文件,特容易踩坑!
ByteBuffer bb = builder.dataBuffer();
File file = new File(fbfilepath);
try {
FileOutputStream fo = new FileOutputStream(file);
fo.write(bb.array(),bb.position(),bb.remaining());
fo.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
这里要特别注意,由于flatbuffers的数据写入方向是反向写入!!因此build结束时数据的开始位置为position标记所在位置,因此要指定write的开始为bb.position(),写入长度为bb.remaining(),否则会导致文件反序列化失败。
反序列化
public static void parse (byte[] bytes) {
ByteBuffer bb = ByteBuffer.wrap(bytes);
ResponseFB responsefb = ResponseFB.getRootAsResponseFB(bb);
int errorno = responsefb.errorNo();
responsefb.message();
ResultFB resultFB = responsefb.result();
resultFB.hasNextPage();
resultFB.dirtag();
int len = resultFB.dataLength();
for (int i = 0; i < len; i++) {
resultFB.data(i).datatype();
ItemDataFB item = resultFB.data(i).itemdata();
String s = item.sname();
item.packageid();
...
int tabslen = item.tabsLength();
for (int j = 0; j < tabslen; j++) {
TabFB tab = item.tabs(j);
tab.f();
tab.type();
}
}
}
这里需要说明的是,序列化过程中每个table中的数据可以无序写入,而反序列化时也不必按序读出,因为扁平二进制的存储方式已经保证了读取方式寻址进行而不会出错。此外,反序列化的速度直接受读取数据多少的影响,由于反序列化不需要进行封包解析的过程,因此数据可以随意读取,而读取的数据个数越多所消耗的时间越长。这也是flatbuffers的妙处所在。
flatbuffers还有很多更有趣的用法等待探索。