Android 上的数据格式 FlatBuffers 介绍

·  阅读 5046
Android 上的数据格式 FlatBuffers 介绍
本文已经翻译成中文《 Android 上的序列化库 FlatBuffers 介绍》,欢迎参加「掘金翻译计划」,翻译优质的技术文章。

JSON - probably everyone knows this lightweight data format used in almost all modern servers. It weights less, is more human-readable and in general is more dev-friendly than old-fashined, horrible xml. JSON is language-independend data format but parsing data and transforming it to e.g. Java objects costs us time and memory resources.
Several days ago Facebook announced big performance improvement in data handling in its Android app. It was connected with dropping JSON format and replacing it with FlatBuffers in almost entire app. Please check this articleto get some basic knowledge about FlatBuffers and results of transition to it from JSON.

While the results are very promising, at the first glance the implementation isn’t too obvious. Also facebook didn’t say too much. That’s why in this post I’d like to show how we can start our work with FlatBuffers.


In short, FlatBuffers is a cross-platform serialization library from Google, created specifically for game development and, as Facebook showed, to follow the 16ms rule of smooth and responsive UI in Android.

But hey, before you throw everything to migrate all your data to FlatBuffers, just make sure that you need this. Sometimes the impact on performance will be imperceptible and sometimes data safety will be more important than a tens of milliseconds difference in computation speed.

What makes FlatBuffers so effective?

  • Serialized data is accessed without parsing because of flat binary buffer, even for hierarchical data. Thanks to this we don’t need to initialize parsers (what means to build complicated field mappings) and parse this data, which also takes time.
  • FlatBuffers data doesn’t need to allocate more memory than it’s used by buffer itself. We don’t need to allocate extra objects for whole hierarchy of parsed data like it’s done in JSON.

For the real numbers just check again facebook article about migrating to FlatBuffers or Google documentation itself.


This article will cover the simplest way of using FlatBuffers in Android app:

  • JSON data is converted to FlatBuffer format somewhere outside the app (e.g. bin ary file is delivered as a file or returned directly from API)
  • Data model (Java classes) is generated by hand, with flatc (FlatBuffer compiler)
  • There are some limitations for JSON file (null fields cannot be used, Date format is parsed as a String)

Probably in the future we’ll prepare more complex solution.

FlatBuffers compiler

At the beginning we have to get flatc - FlatBuffers compiler. It can be built from source code hosted in Google’s flatbuffers repository. Let’s download/clone it. Whole build process is described on FlatBuffers Building documentation. If you are Mac user all you have to do is:

  1. Open downloaded source code on \{extract directory}\build\XcodeFlatBuffers.xcodeproj
  2. Run flatc scheme (should be selected by default) by pressing Play button or ⌘ + R
  3. flatc executable will appear in project root directory.

Now we’re able to use schema compiler which among the others can generate model classes for given schema (in Java, C#, Python, GO and C++) or convert JSON to FlatBuffer binary file.

Schema file

Now we have to prepare schema file which defines data structures we want to de-/serialize. This schema will be used with flatc to create Java models and to transform JSON into Flatbuffer binary file.

Here is a part of our JSON file:

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

Full version is available here. It’s a bit modified version of data which can be taken from Github API call:….

Writing a FlatBuffer schema is very well documented, so I won’t delve into this. Also in our case schema won’t be very complicated. All we have to do is to create 3 tables: ReposList, Repo and User, and define root_type. Here is the important part of this schema:

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;复制代码

Full schema file is available here.

FlatBuffers data file

Great, now all we have to do is to convert repos_json.json to FlatBuffers binary file and generate Java models which will be able to represent our data in Java-friendly style (all files required in this operation are available in our repository):

$ ./flatc -j -b repos_schema.fbs repos_json.json

If everything goes well, here is a list of generated files:

  • repos_json.bin (we’ll rename it to repos_flat.bin)
  • Repos/
  • Repos/
  • Repos/

Android app

Now let’s create our example app to check how FlatBuffers format works in practice. Here is the screenshot of it:


ProgressBar will be used only to show how incorrect data handling (in UI thread) can affect smoothness of user interface.

app/build.gradle file of our app will look like this:

apply plugin: ''
apply plugin: 'com.jakewharton.hugo'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.0 rc2"

    defaultConfig {
        applicationId ""
        minSdkVersion 15
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), ''

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile ''
    compile ''
    compile 'com.jakewharton:butterknife:7.0.1'
    compile 'io.reactivex:rxjava:1.0.10'
    compile 'io.reactivex:rxandroid:1.0.0'

Of course it’s not necessary to use Rx or ButterKnife in our example, but why not to make this app a bit nicer 😉 ?

Let’s put repos_flat.bin and repos_json.json files to res/raw/ directory.

Here is RawDataReader util which helps us to read raw files in Android app.

At the end put Repo, ReposList and User somewhere in project’s source code.

FlatBuffers library

FlatBuffers provides java library to handle this data format directly in java. Here is flatbuffers-java-1.2.0-SNAPSHOT.jar file. If you want to generate it by hand you have to move back to downloaded FlatBuffers source code, go to java/ directory and use Maven to generate this library:

$ mvn install

Now put .jar file to your Android project, into app/libs/ directory.

Great, now all we have to do is to implement MainActivity class. Here is the full source code of it:

public class MainActivity extends AppCompatActivity {

    TextView tvFlat;
    TextView tvJson;

    private RawDataReader rawDataReader;

    private ReposListJson reposListJson;
    private ReposList reposListFlat;

    protected void onCreate(Bundle savedInstanceState) {
        rawDataReader = new RawDataReader(this);

    public void onJsonClick() {
        rawDataReader.loadJsonString(R.raw.repos_json).subscribe(new SimpleObserver() {
            public void onNext(String 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: " +;
        long endTime = System.currentTimeMillis() - startTime;
        tvJson.setText("Elements: " + reposListJson.repos.size() + ": load time: " + endTime + "ms");

    public void onFlatBuffersClick() {
        rawDataReader.loadBytes(R.raw.repos_flat).subscribe(new SimpleObserver() {
            public void onNext(byte[] bytes) {

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


Methods which should interest us the most:

  • parseReposListJson(String reposStr) - this method initializes Gson parser and convert json String to Java objects.
  • loadFlatBuffer(byte[] bytes) - this method converts bytes (our repos_flat.bin file) to Java objects.


Now let’s visualize differences between JSON and FlatBuffers loading time and consumed resources. Tests are made on Nexus 5 with Android M (beta) installed.

Loading time

Measured operation is conversion to Java files and iteration over all (90) elements.

JSON - 200ms (range: 180ms - 250ms) - average loading time of our JSON file (weight: 478kB) FlatBuffers - 5ms (range: 3ms - 10ms) - average loading time of FlatBuffers binary file (weight: 362kB)

Remember our 16ms rule ? We’re calling those method with a reason in UI thread. Take a look at how our interface would behave in this case:

JSON loading


FlatBuffer loading


See the difference? Json loading freezes ProgressBar for a while, making our interface unpleasant (operation takes more than 16ms).

Allocations, CPU etc.

Whant to measure more? Maybe it’s a good time to give a try to Android Studio 1.3 and new features like Allocation Tracker, Memory Viewer and Method Tracer.

Source code

Full source code of described project is available on Github repository. You don’t need to deal with FlatBuffers project - all you need is in flatbuffers/ directory.

Miroslaw Stanek
Head of Mobile Development @ Azimo Money Transfer

If you liked this post, you can share it with your followers or follow me on Twitter!

已添加到「」, 点击更改