FlatBuffers在android的使用简介

mac环境上编辑器flatc生成

参考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。

Java文件编译

./flatc -j -o ~/workspace/testfb repos_schema.fbs

-j表示生成java文件,–o 表示将文件编译到指定目录~/workspace/testfb下,schema文件的位置放在命令的最后。
命令详细用法可见官方文档

schema语法

可见官方文档

源码剖析

参见android FlatBuffers剖析

重点:扁平二进制的存储方式。

  • 使用bytebuffer操作byte数据
  • 逆向存储,将数据概要信息放在整个buffer的头部。这也回答了作者文中的疑问,为什么要费劲的反向存储,即从buffer的尾部开始写数据。
  • offset的概念,每个数据距离buffer尾部的长度。
  • vtable中存储的是某个数据相对于vtable的偏移。寻址时只要将当前pos+偏移就得到真实数据信息摘要所在位置。因此,根据roottype的数据Vtable,就可以找到一切。

androidstudio中的使用示例

  1. gradle添加依赖
compile 'com.github.davidmoten:flatbuffers-java:1.4.0.1'
  1. 定义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"
  }
}
  1. 编写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的前后兼容机制。
注意:

  • 只能在表定义的末尾添加新字段。较旧的数据仍将正确读取,并在读取时给予默认值。
  • 不能从模式中删除不再使用的字段,可以用deprecated标记它们,这将阻止在生成class中生成访问器,以此来强制不再使用该字段。

根类型
root_type必须指定,序列化数据的根结构,解析开始的位置。

  1. flatc编译后生成文件目录如下


    编译生成文件目录.png
  2. 将编译生成的文件导入工程即可开始编码了。

序列化示例

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还有很多更有趣的用法等待探索。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容