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

相关文章
相关标签/搜索