Android FlatBuffers數據交互

FlatBuffers簡介

FlatBuffers是Google開源的一個跨平臺的、高效的、提供了C++/Java接口的序列化工具庫,它是Google專門爲遊戲開發或其餘性能敏感的應用程序需求而建立。尤爲適用移動,嵌入式平臺,這些平臺在內存大小及帶寬相比桌面系統都是受限的,而應用程序好比遊戲又有更高的性能要求。它將序列化數據存儲在緩存中,這些數據既能夠存儲在文件中,又能夠經過網絡原樣傳輸,而不須要任何解析開銷。如下是項目地址: 代碼託管主頁:github.com/google/flat… 項目介紹主頁:google.github.io/flatbuffers…html

FlatBuffers優點

相比傳統的JSON和Protocol Buffers等序列化工具,FlatBuffers具備以下的一些優勢:java

  • 不須要解析/拆包就能夠訪問序列化數據:FlatBuffers與其餘庫不一樣之處就在於它使用二進制緩衝文件來表示層次數據,這樣它們就能夠被直接訪問而不需解析與拆包,同時還支持數據結構進化(前進、後退兼容性)。
  • 內存高效速度快 :訪問數據時只須要訪問內存中的緩衝區。它不須要多餘的內存分配(至少在C++是這樣,其餘語言中可能會有變更)。 FlatBuffers還適合配合 mmap或數據流使用,只須要緩衝區的一部分存儲在內存中。訪問時速度接近原結構訪問,只有一點延遲(一種虛函數表vtable),是爲了容許格式升級以 及可選字段。FlatBuffers適合那些花費了大量時間和空間(內存分配)來訪問和構建序列化數據的項目,好比遊戲以及其餘對錶現敏感的應用。能夠參考FlatBuffers基準
  • 靈活 :因爲具備可選字段,你不但有很強的升級和回退兼容性(對於歷史悠久的遊戲尤爲重要,不用爲了每一個版本升級全部數據),在選擇要存儲哪些數據以及設計數據結構時也很自由。
  • 輕量的code footprint:FlatBuffers只須要不多量的生成代碼,以及一個表示最小依賴的很小的頭文件,很容易集成。
  • 強類型:當編譯時報錯時,不須要本身寫重複的容易出錯的運行時檢查,它能夠自動生成有用的代碼。
  • 使用方便:生成的C++代碼容許精簡訪問與構建代碼,還有可選的用於實現圖表解析、相似JSON的運行時字符串展現等功能的方法。(後者比JSON解析庫更快,內存效率更高)。
  • 代碼跨平臺且沒有依賴:C++代碼能夠運行在任何近代的gcc/clang和VS2010上,同時還有用於測試和範例的構建文件(Android中.mk文件,其餘平臺是cmake文件)。

VS Protocol Buffers和JSON

Protocol Buffers的確和FlatBuffers比較相似,但其主要區別在於FlatBuffers在訪問數據前不須要解析/拆包這一步,並且Protocol Buffers既沒有可選的文本導入/導出功能,也沒有Schemas語法特性(好比union)。git

JSON是一種輕量級的數據交換格式,JSON 能夠將 JavaScript 對象中表示的一組數據轉換爲字符串,而後就能夠在函數之間輕鬆地傳遞這個字符串,或者在異步應用程序中將字符串從 Web 客戶機傳遞給服務器端程序。JSON和動態類型語言(如JavaScript)一塊兒使用時很是方便。然而在靜態類型語言中序列化數據時,JSON不但具備運行效率低的明顯缺點,並且會讓你寫更多的代碼來訪問數據。github

FlatBuffers究竟有多提升

  • 解析速度:解析一個20KB的JSON流須要35ms,超過了UI刷新間隔也就是16.6ms。若是解析JSON的話,咱們就在滑動時就會由於要從磁盤加載緩存而致使掉幀(視覺上的卡頓)。
  • 解析器初始化 :一個JSON解析器須要先構建字段映射再進行解析,這會花100ms到200ms,很明顯的拖緩App啓動時間。
  • 垃圾回收 在解析JSON時建立了不少小對象,在咱們的試驗中,解析20kb的JSON流時,要分配大約100kb的瞬時存儲,對Java內存回收形成很大壓力。

FlatBuffers實戰

FlatBuffers運做流程

首先來看一下FlatBuffers項目爲開發者提供了哪些內容,能夠從官網下載源碼,其目錄結構以下圖: json

在這裏插入圖片描述
若是要將FlatBuffers 用到咱們的項目中,又須要哪些流程呢?能夠參考下面的流程圖:
在這裏插入圖片描述

FlatBuffers用法

就像Parcel和Serializable的序列化同樣,FlatBuffers的是使用方式上也比最傳統的JSON序列化要複雜的多。在實際上面開發中,爲了下降開發的難度,提升開發效率,咱們會將源碼編譯成可植入的第三方庫。下面以Java環境爲例,來介紹FlatBuffers的簡單使用方法。讀者能夠到對應的maven倉庫下載緩存

如今,假如咱們拿到的json文件的格式是下面這樣的:bash

{
  "repos": [
    {
      "id": 27149168,
      "name": "acai",
      "full_name": "google/acai",
      "owner": {
        "login": "google",
        "id": 1342004,
        ...
        "type": "Organization",
        "site_admin": false
      },
      "private": false,
      "html_url": "https://github.com/google/acai",
      "description": "Testing library for JUnit4 and Guice.",
      ...
      "watchers": 21,
      "default_branch": "master"
    },
    ...
  ]
}

複製代碼

注:能夠經過下面的連接來獲取更完整的json對象服務器

模式文件

咱們須要準備一個model文件,它定義了咱們想要序列化/反序列化 的數據結構,這個模式將被flatc用於建立Java模型以及從JSON到FlatBuffer二進制文件的轉換。網絡

如今,咱們所要作的全部事情就是建立3個表:ReposList,Repo和User,並定義root_type。例如:數據結構

table ReposList {
    repos : [Repo];
}

table Repo {
    id : long;
    name : string;
    full_name : string;
    owner : User;
    //...
    labels_url : string (deprecated);
    releases_url : string (deprecated);
}

table User {
    login : string;
    id : long;
    avatar_url : string;
    gravatar_id : string;
    //...
    site_admin : bool;
}

root_type ReposList;
複製代碼

注:完整的模式文件能夠點擊下面的連接來獲取

FlatBuffers文件

接下來,咱們所須要作的就是將repos_json.json轉換爲FlatBuffers二進制文件,併產生Java模型,其能夠以Java友好的方式表示咱們的數據,下面是轉換的命令:

$ ./flatc -j -b repos_schema.fbs repos_json.json
複製代碼

若是沒有任何報錯,將會生成以下4個文件:

repos_json.bin (將被重命名爲repos_flat.bin)
Repos/Repo.java
Repos/ReposList.java
Repos/User.java
複製代碼

測試

接下來,咱們可使用FlatBuffers提供的Java庫來處理在Java中直接處理這種數據格式,此處使用須要使用到 flatbuffers-java-1.2.0-SNAPSHOT.jar

public class MainActivity extends AppCompatActivity {

    @Bind(R.id.tvFlat)
    TextView tvFlat;
    @Bind(R.id.tvJson)
    TextView tvJson;

    private RawDataReader rawDataReader;

    private ReposListJson reposListJson;
    private ReposList reposListFlat;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        rawDataReader = new RawDataReader(this);
    }

    @OnClick(R.id.btnJson)
    public void onJsonClick() {
        rawDataReader.loadJsonString(R.raw.repos_json).subscribe(new SimpleObserver<String>() {
            @Override
            public void onNext(String reposStr) {
                parseReposListJson(reposStr);
            }
        });
    }

    private void parseReposListJson(String reposStr) {
        long startTime = System.currentTimeMillis();
        reposListJson = new Gson().fromJson(reposStr, ReposListJson.class);
        for (int i = 0; i < reposListJson.repos.size(); i++) {
            RepoJson repo = reposListJson.repos.get(i);
            Log.d("FlatBuffers", "Repo #" + i + ", id: " + repo.id);
        }
        long endTime = System.currentTimeMillis() - startTime;
        tvJson.setText("Elements: " + reposListJson.repos.size() + ": load time: " + endTime + "ms");
    }

    @OnClick(R.id.btnFlatBuffers)
    public void onFlatBuffersClick() {
        rawDataReader.loadBytes(R.raw.repos_flat).subscribe(new SimpleObserver<byte[]>() {
            @Override
            public void onNext(byte[] bytes) {
                loadFlatBuffer(bytes);
            }
        });
    }

    private void loadFlatBuffer(byte[] bytes) {
        long startTime = System.currentTimeMillis();
        ByteBuffer bb = ByteBuffer.wrap(bytes);
        reposListFlat = frogermcs.io.flatbuffs.model.flat.ReposList.getRootAsReposList(bb);
        for (int i = 0; i < reposListFlat.reposLength(); i++) {
            Repo repos = reposListFlat.repos(i);
            Log.d("FlatBuffers", "Repo #" + i + ", id: " + repos.id());
        }
        long endTime = System.currentTimeMillis() - startTime;
        tvFlat.setText("Elements: " + reposListFlat.reposLength() + ": load time: " + endTime + "ms");

    }
}
複製代碼

在上面的示例代碼中,有兩個方法是比較核心的,須要咱們注意。

  • parseReposListJson(String reposStr) :初始化Gson解析器並將json字符串轉換爲Java對象。
  • loadFlatBuffer(byte[] bytes) 將bytes(即repos_flat.bin文件)轉爲Java對象。

耗時測試

下面咱們來測試下FlatBuffers和傳統的json在數據解析上的耗時,咱們以4mb的JSON文件爲例。

在這裏插入圖片描述
如圖,能夠發現FlatBuffers花了1-5ms,JSON花了大約2000ms。而且FlatBuffers期間Android App中沒有GC,而在使用JSON時發生了不少次GC,測試的源碼能夠經過如下地址下載: FlatBuffers耗時測試

參考:在Android中使用FlatBuffers FlatBuffers官方doc

相關文章
相關標籤/搜索