Android FlatBuffers实战

2023-11-21 08:50
文章标签 实战 android flatbuffers

本文主要是介绍Android FlatBuffers实战,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

FlatBuffers简介

FlatBuffers是Google开源的一个跨平台的、高效的、提供了C++/Java接口的序列化工具库,它是Google专门为游戏开发或其他性能敏感的应用程序需求而创建。尤其适用移动,嵌入式平台,这些平台在内存大小及带宽相比桌面系统都是受限的,而应用程序比如游戏又有更高的性能要求。它将序列化数据存储在缓存中,这些数据既可以存储在文件中,又可以通过网络原样传输,而不需要任何解析开销。以下是项目地址:
代码托管主页:https://github.com/google/flatbuffers;
项目介绍主页:http://google.github.io/flatbuffers/index.html;

FlatBuffers优势

相比传统的JSON和Protocol Buffers等序列化工具,FlatBuffers具有如下的一些优点:

  • 不需要解析/拆包就可以访问序列化数据: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)。

JSON是一种轻量级的数据交换格式,JSON 可以将 JavaScript 对象中表示的一组数据转换为字符串,然后就可以在函数之间轻松地传递这个字符串,或者在异步应用程序中将字符串从 Web 客户机传递给服务器端程序。JSON和动态类型语言(如JavaScript)一起使用时非常方便。然而在静态类型语言中序列化数据时,JSON不但具有运行效率低的明显缺点,而且会让你写更多的代码来访问数据。

FlatBuffers究竟有多提高

  • 解析速度:解析一个20KB的JSON流需要35ms,超过了UI刷新间隔也就是16.6ms。如果解析JSON的话,我们就在滑动时就会因为要从磁盘加载缓存而导致掉帧(视觉上的卡顿)。
  • 解析器初始化 :一个JSON解析器需要先构建字段映射再进行解析,这会花100ms到200ms,很明显的拖缓App启动时间。
  • 垃圾回收 在解析JSON时创建了很多小对象,在我们的试验中,解析20kb的JSON流时,要分配大约100kb的瞬时存储,对Java内存回收造成很大压力。

FlatBuffers实战

FlatBuffers运作流程

首先来看一下FlatBuffers项目为开发者提供了哪些内容,可以从官网下载源码,其目录结构如下图:
在这里插入图片描述
如果要将FlatBuffers 用到我们的项目中,又需要哪些流程呢?可以参考下面的流程图:
在这里插入图片描述

FlatBuffers用法

就像Parcel和Serializable的序列化一样,FlatBuffers的是使用方式上也比最传统的JSON序列化要复杂的多。在实际上面开发中,为了降低开发的难度,提高开发效率,我们会将源码编译成可植入的第三方库。下面以Java环境为例,来介绍FlatBuffers的简单使用方法。读者可以到对应的maven仓库下载。

现在,假如我们拿到的json文件的格式是下面这样的:

{"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;@Overrideprotected 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>() {@Overridepublic 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[]>() {@Overridepublic 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

这篇关于Android FlatBuffers实战的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/xiangzhihong8/article/details/83376811
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/401223

相关文章

MySQL 多列 IN 查询之语法、性能与实战技巧(最新整理)

《MySQL多列IN查询之语法、性能与实战技巧(最新整理)》本文详解MySQL多列IN查询,对比传统OR写法,强调其简洁高效,适合批量匹配复合键,通过联合索引、分批次优化提升性能,兼容多种数据库... 目录一、基础语法:多列 IN 的两种写法1. 直接值列表2. 子查询二、对比传统 OR 的写法三、性能分析

Python办公自动化实战之打造智能邮件发送工具

《Python办公自动化实战之打造智能邮件发送工具》在数字化办公场景中,邮件自动化是提升工作效率的关键技能,本文将演示如何使用Python的smtplib和email库构建一个支持图文混排,多附件,多... 目录前言一、基础配置:搭建邮件发送框架1.1 邮箱服务准备1.2 核心库导入1.3 基础发送函数二、

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

PowerShell中15个提升运维效率关键命令实战指南

《PowerShell中15个提升运维效率关键命令实战指南》作为网络安全专业人员的必备技能,PowerShell在系统管理、日志分析、威胁检测和自动化响应方面展现出强大能力,下面我们就来看看15个提升... 目录一、PowerShell在网络安全中的战略价值二、网络安全关键场景命令实战1. 系统安全基线核查

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

Java MQTT实战应用

《JavaMQTT实战应用》本文详解MQTT协议,涵盖其发布/订阅机制、低功耗高效特性、三种服务质量等级(QoS0/1/2),以及客户端、代理、主题的核心概念,最后提供Linux部署教程、Sprin... 目录一、MQTT协议二、MQTT优点三、三种服务质量等级四、客户端、代理、主题1. 客户端(Clien

在Spring Boot中集成RabbitMQ的实战记录

《在SpringBoot中集成RabbitMQ的实战记录》本文介绍SpringBoot集成RabbitMQ的步骤,涵盖配置连接、消息发送与接收,并对比两种定义Exchange与队列的方式:手动声明(... 目录前言准备工作1. 安装 RabbitMQ2. 消息发送者(Producer)配置1. 创建 Spr

深度解析Spring Boot拦截器Interceptor与过滤器Filter的区别与实战指南

《深度解析SpringBoot拦截器Interceptor与过滤器Filter的区别与实战指南》本文深度解析SpringBoot中拦截器与过滤器的区别,涵盖执行顺序、依赖关系、异常处理等核心差异,并... 目录Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实