FlatBuffers在android的使用簡(jiǎn)介

mac環(huán)境上編輯器flatc生成

參考http://blog.csdn.net/yxz329130952/article/details/50706369

  • 下載flatbuffers的源碼,解壓
  • homebrew下載安裝cmake
  • cd到flatbuffers的源碼文件夾
  • 開(kāi)始編譯flatc
cd flatbuffers/
cmake -G "Unix Makefiles"
make

指定生成的對(duì)象為Unix makefile,以便cmake根據(jù)CMakeLists.txt文件生成對(duì)應(yīng)的makefile文件。(在當(dāng)前目錄下可以看到生成的makefile文件,直接執(zhí)行make命令即可得到flatc。

Java文件編譯

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

-j表示生成java文件,–o 表示將文件編譯到指定目錄~/workspace/testfb下,schema文件的位置放在命令的最后。
命令詳細(xì)用法可見(jiàn)官方文檔

schema語(yǔ)法

可見(jiàn)官方文檔

源碼剖析

參見(jiàn)android FlatBuffers剖析

重點(diǎn):扁平二進(jìn)制的存儲(chǔ)方式。

  • 使用bytebuffer操作byte數(shù)據(jù)
  • 逆向存儲(chǔ),將數(shù)據(jù)概要信息放在整個(gè)buffer的頭部。這也回答了作者文中的疑問(wèn),為什么要費(fèi)勁的反向存儲(chǔ),即從buffer的尾部開(kāi)始寫(xiě)數(shù)據(jù)。
  • offset的概念,每個(gè)數(shù)據(jù)距離buffer尾部的長(zhǎng)度。
  • vtable中存儲(chǔ)的是某個(gè)數(shù)據(jù)相對(duì)于vtable的偏移。尋址時(shí)只要將當(dāng)前pos+偏移就得到真實(shí)數(shù)據(jù)信息摘要所在位置。因此,根據(jù)roottype的數(shù)據(jù)Vtable,就可以找到一切。

androidstudio中的使用示例

  1. gradle添加依賴
compile 'com.github.davidmoten:flatbuffers-java:1.4.0.1'
  1. 定義schema
    我們以這個(gè)數(shù)據(jù)樣式(Json)為例
{
  "error_no":0,
  "message":"",
  "result":{
    "data":[
      {
        "datatype":1,
        "itemdata":
            {//共有字段45個(gè)
              "sname":"\u5fae\u533b",
              "packageid":"330611",
              …
              "tabs":[
                        {
                          "type":1,
                          "f":"abc"
                        },
                        …
              ]
            }
      },
      …
    ],
    "hasNextPage":true,
    "dirtag":"soft"
  }
}
  1. 編寫(xiě)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;

就以上示例簡(jiǎn)單講解一下:
命名空間
namespace決定了編譯生成文件的存放位置,類(lèi)似c的寫(xiě)法,同樣,不同的schema文件可以互相引用,通過(guò)include的方式
table-表
表是在FlatBuffers中定義對(duì)象的主要方式,它由一個(gè)名稱和一個(gè)字段列表組成。每個(gè)字段都有一個(gè)名稱,一個(gè)類(lèi)型和可選的默認(rèn)值(如果省略,則默認(rèn)為0/ NULL)。
每個(gè)字段都是可選的,因此,可以靈活地添加字段,而不必?fù)?dān)心數(shù)據(jù)膨脹。此設(shè)計(jì)也是FlatBuffer的前后兼容機(jī)制。
注意:

  • 只能在表定義的末尾添加新字段。較舊的數(shù)據(jù)仍將正確讀取,并在讀取時(shí)給予默認(rèn)值。
  • 不能從模式中刪除不再使用的字段,可以用deprecated標(biāo)記它們,這將阻止在生成class中生成訪問(wèn)器,以此來(lái)強(qiáng)制不再使用該字段。

根類(lèi)型
root_type必須指定,序列化數(shù)據(jù)的根結(jié)構(gòu),解析開(kāi)始的位置。

  1. flatc編譯后生成文件目錄如下


    編譯生成文件目錄.png
  2. 將編譯生成的文件導(dǎo)入工程即可開(kāi)始編碼了。

序列化示例

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,每個(gè)Data下有多個(gè)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,多個(gè)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);
           //將當(dāng)前data數(shù)據(jù)的offset寫(xiě)入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);
        //調(diào)用finish結(jié)束此次構(gòu)造
        builder.finish(responseOfset);
    }

序列化后的數(shù)據(jù)寫(xiě)入文件,特容易踩坑!

        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的數(shù)據(jù)寫(xiě)入方向是反向?qū)懭?/strong>?。∫虼薭uild結(jié)束時(shí)數(shù)據(jù)的開(kāi)始位置為position標(biāo)記所在位置,因此要指定write的開(kāi)始為bb.position(),寫(xiě)入長(zhǎng)度為bb.remaining(),否則會(huì)導(dǎo)致文件反序列化失敗。

反序列化

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

這里需要說(shuō)明的是,序列化過(guò)程中每個(gè)table中的數(shù)據(jù)可以無(wú)序?qū)懭?,而反序列化時(shí)也不必按序讀出,因?yàn)楸馄蕉M(jìn)制的存儲(chǔ)方式已經(jīng)保證了讀取方式尋址進(jìn)行而不會(huì)出錯(cuò)。此外,反序列化的速度直接受讀取數(shù)據(jù)多少的影響,由于反序列化不需要進(jìn)行封包解析的過(guò)程,因此數(shù)據(jù)可以隨意讀取,而讀取的數(shù)據(jù)個(gè)數(shù)越多所消耗的時(shí)間越長(zhǎng)。這也是flatbuffers的妙處所在。
flatbuffers還有很多更有趣的用法等待探索。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容