Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

protobuf Protocol Buffers 简介 案例


目录

简介

案例

GitHub

在Java中使用

官网--需翻墙

  • Protocol Buffers (a.k.a., protobuf) 是Google开发的一种用于序列化结构化数据(比如Java中的Object,C中的Structure)的语言中立、平台中立、可扩展数据描述语言,可用于数据存储、通信协议等方面。
  • Protocol Buffers可以理解为是更快、更简单、更小的JSON或者XML,区别在于Protocol Buffers是二进制格式,而JSON和XML是文本格式
  • 目前 protobuf 支持的语言包括:C++、C#、Java、JS、OC、PHP、Ruby这七种。

优缺点

Protobuf 相对于 XML 的优点

  • 简洁,具有更少的歧义性
  • 体积小,消息大小只需要XML的1/10 ~ 1/3
  • 速度快,解析速度比XML快20 ~ 100
  • 自动生成数据访问类,方便应用程序的使用
  • 更好的兼容性,能够很好的支持向下或向上兼容

注意:protobuf传输的是对象的二进制内容,而非编译前的 .proto文件,更非编译后的Java源文件,.proto文件只是为了方便阅读、调试和编辑用的。而json和xml传输的就是你看到的json和xml文本内容(字符串)

Protobuf 的优点

  • Protobuf 有如 XML,不过它更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。
  • 它有一个非常棒的特性,即向后兼容性好,人们不必破坏已部署的、依靠“老”数据格式的程序就可以对数据结构进行升级。这样您的程序就可以不必担心因为消息结构的改变而造成的大规模的代码重构或者迁移的问题。因为添加新的消息中的 field 并不会引起已经发布的程序的任何改变。
  • Protobuf 语义更清晰,无需类似 XML 解析器的东西,因为 Protobuf 编译器会将 .proto 文件编译生成对应的数据访问类以对 Protobuf 数据进行序列化、反序列化操作。
  • 使用 Protobuf 无需学习复杂的文档对象模型,Protobuf 的编程模式比较友好,简单易学,同时它拥有良好的文档和示例,对于喜欢简单事物的人们而言,Protobuf 比其他的技术更加有吸引力。

Protobuf 的不足

  • Protbuf 与 XML 相比也有不足之处。它功能简单,无法用来表示复杂的概念
  • XML 已经成为多种行业标准的编写工具,Protobuf 只是 Google 公司内部使用的工具,在通用性上还差很多。
  • Protobuf 不适合用来描述一个基于文本的标记型文档(比如HTML),因为你无法轻易的交错文本的结构。另外,XML具有很好的可读性和可编辑性,而protocol buffers,至少在它们的原生形式上并不具备这个特点。XML同时也是可扩展、自描述的,而一个protocol buffer只有在具有message 定义(在.proto文件中定义)时才会有意义。

个人觉得在 Android 应用中,protocol buffer 有一个致命的缺点,那就是生成的 java 文件太大,会是我们的 apk 体积增大很多!例如,在我之前做过的 ogod 项目中,由pb生成的十几个Java类,体积竟然有几 MB ,其大小几乎等于其余四百多个类的总和。



proto3简介

我们最新的版本version 3 alpha release引进了一个新的语言版本--Protocol Buffers version 3 (称之为proto3),它在我们现存的语言版本(proto2)上引进了一些新特性。proto3简化了protocol buffer language,这使其可以更便于使用和支持更多的编程语言:我们现在的alpha release版本可以让你能产生JAVA、C++、Pthyon、JavaNano、Ruby、Objective-C和C#版本的protocol buffer code,不过可能有时会有一些局限性。另外,你可以使用最新的Go protoc插件来产生Go语言版本的proto3 code,这可以从golang/protobuf Github repository获取。

以下情况下,我们现在只推荐你使用proto3:

  • 如果你想尝试在我们新支持的语言中使用protocol buffers
  • 如果你想尝试我们最新开源的RPC实现gRPC(目前仍处于alpha release版本),我们建议你为所有的 gRPC 服务器和客户端都使用proto3以避免兼容性问题。

注意两个版本的语言APIs并不是完全兼容的,为了避免给原来的用户造成不便,我们将会继续维护之前的那个proto2版本。

使用步骤

下载 protobuf 编译器

下载路径

注意这个东西在每个版本的下载部分的最下面,例如 protoc-3.6.1-win32.zip

它包含 protoc 二进制文件以及与 protobuf 一起分发的一组标准 .proto文件。

解压后可以把protoc.exe所在的目录加入系统变量Path,例如【C:\Android\protobuf\protoc-3.6.1-win32\bin】

然后可以在任何路径测试protoc命令是否可用,例如:

protoc --version

libprotoc 3.6.1

编写 .proto 文件

可以在 PC 任意位置通过任意纯文本编辑器编写 .proto 文件,例如:

syntax = "proto3";//指定编译时所使用的语法版本,不指定时会有 WARNING 提示:默认使用 proto2。
option java_package = "com.bqt.test.protomodel";//生成的类的【包名】
option java_outer_classname = "PersonEntity";//生成的类的【类名】
message Person { //真正用来封装数据的【内部类】的类名
int32 id = 1;
string name = 2;
string email = 3;
}

编译 .proto 文件生成 java 类

通过protoc命令编译.proto文件生成java类:

命令:protoc -I=目录1 --java_out=目录2 文件3

  • 【-I=目录1】:输入目录。这个参数是简称,全称为:【--proto_path=目录1】。可同时指定多个,目录将按顺序搜索。如果没有给出,则使用当前工作目录。
  • 【--java_out=目录2】:输出目录,指定生成的Java源文件的目录。
  • 【文件3】:输入文件
    • 如果指定了相对文件路径,则将在工作目录[working directory]中搜索该文件。
    • 选项 --proto_path 不会影响搜索此参数文件的方式。
    • 文件内容将在参数列表中的 @<filename> 位置展开。
    • 请注意,shell扩展不会应用于文件的内容(即,您不能使用引号、通配符、转义、命令[quotes, wildcards, escapes, commands]等)。
    • 每行对应一个参数,即使它包含空格。

生成 Java 类时使用【java_out】,生成其他语言的结构化数据时只需更改这个参数即可,例如如果用在iOS上,可以使用【objc_out】,这就是其跨平台的体现。

使用案例:protoc -I=D:\bqt\proto --java_out=D:\bqt\out test.proto

protoc -I=D:\bqt\proto --java_out=D:\bqt\out test.proto

如果在当前目录执行命令,可以简化为:protoc --java_out=D:\bqt\out test.proto

执行上面代码后会发现,在指定的目录下面生成了包含包名PersonEntity.java类(虽然定义很简单,但是生成的类有将近1000行代码)。



项目中添加 protobuf runtime

确保 runtime 的版本号与 protoc 的版本号匹配(或者版本更新)。

方式一:下载源码(下载路径同上) ,下载后将需要的源码连同包名一起复制到项目中。

需要的源码一般在以下目录中:

protobuf-3.6.1\java\core\src\main\java\com\google\protobuf    //核心包
protobuf-3.6.1\java\util\src\main\java\com\google\protobuf\util //可选包,提供 JsonFormat 等功能

方式二:直接添加protobuf依赖即可,版本号要比使用的 protoc.exe 版本相同或更高:

implementation 'com.google.protobuf:protobuf-java:3.6.1'//protobuf
//implementation 'com.google.protobuf:protobuf-lite:3.0.1'//protobuf lite
//implementation 'com.google.protobuf:protobuf-java-util:3.6.1'//可选,JsonFormat 等功能

测试 java 类

将上面生成的 java 类连同包名复制到项目中,然后就可以很方便的调用封装好的Java类的API了。

public class MainActivity extends ListActivity {

    private static final String PROTO_FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/test_proto";
private static final String JSON_FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/test_json"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] array = {"以proto格式保存对象",
"解析proto方式保存的对象内容",
"以Json格式保存对象",
"解析son方式保存的对象内容",};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Arrays.asList(array)));
} @Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
PersonEntity.Person protoPerson = PersonEntity.Person.newBuilder()
.setId(3)
.setName("zhangsan")
.setEmail("test@qq.com")
.build();
Log.i("bqt", "原始的proto格式对象内容:" + protoPerson.toString());
FileUtils.writeToFile(protoPerson.toByteArray(), PROTO_FILE_PATH);//以proto格式保存对象,保存的长度=25
break;
case 1:
byte[] protoFileBytes = FileUtils.readFromFile(PROTO_FILE_PATH);
try {
PersonEntity.Person parseProtoPerson = PersonEntity.Person.parseFrom(protoFileBytes);
Log.i("bqt", "解析proto方式保存的对象内容:" + parseProtoPerson.toString());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
break;
case 2:
Person jsonPerson = Person.newBuilder().id(3).name("zhangsan").email("test@qq.com").build();
Log.i("bqt", "原始的Json格式对象内容:" + jsonPerson.toString());
FileUtils.writeToFile(new Gson().toJson(jsonPerson).getBytes(), JSON_FILE_PATH);//以Json格式保存对象,保存的长度=48
break;
case 3:
byte[] jsonFilebytes = FileUtils.readFromFile(JSON_FILE_PATH);
String json = new String(jsonFilebytes);
Person parseJsonPerson = new Gson().fromJson(json, Person.class);
Log.i("bqt", parseJsonPerson.toString());
break;
default:
break;
}
}
}

在 Android 中使用 Java Lite runtime

使用说明

对于Android用户,为了更小的代码大小,建议使用 protobuf 的Java Lite runtime。Java Lite runtime 也可以与Proguard一起使用,因为它不依赖于Java反射,并且已经过优化以允许尽可能多的代码剥离[code stripping]。

要使用 Java Lite runtime,除了和上面一样需要安装 protobuf 编译器外,还需要安装 protoc-gen-javalite 插件。

同样选择适用于您的平台的版本,例如protoc-gen-javalite-3.0.0-windows-x86_64.exe,然后将其重命名为protoc-gen-javalite.exe,并在 PATH 中配置其路径,建议将其和 protoc 放在同一目录中:

之后,只需使用【javalite_out】代替【java_out】,您就可以和上面一样编译 .proto 文件生成 Java Lite 代码,例如:

//protoc -I=D:\bqt\proto --java_out=D:\bqt\out test.proto
protoc -I=D:\bqt\proto --javalite_out=D:\bqt\out test.proto

对比两次生成的文件我们发现,Java Lite runtime 生成的 Java 类虽然明显比之前小了很多,但是仍旧比较大!



同样,项目中应该添加 Java Lite 的源文件(或jar包)或添加 Java Lite 的依赖:

implementation 'com.google.protobuf:protobuf-lite:3.0.1'//protobuf lite

这里的版本号要比使用的 protoc 插件版本相同或更高(文档中说只要大于3.0.0就可以)。

以下内容我还没去研究

Use Protobuf Java Lite Runtime with Bazel:

Bazel has native build rules to work with protobuf。对于Java Lite运行时,您可以使用java_lite_proto_library规则。 查看我们的构建文件示例以了解如何使用它。

2018-8-17

protobuf Protocol Buffers 简介 案例 MD的更多相关文章

  1. LruCache DiskLruCache 缓存 简介 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. Google Protocol Buffers简介

    什么是 protocol buffers ? Protocol buffers 是一种灵活.高效的序列化结构数据的自动机制--想想XML,但是它更小,更快,更简单.你只需要把你需要怎样结构化你的数据定 ...

  3. 集合 enum 枚举 简介 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  4. protobuf(Protocol Buffers)java初体验

    因为项目须要所以简单的研究了下protobuf.我也是參照网上的博客,所以大部分内容我也就不反复造轮子了.首先protobuf介绍点击这里,使用介绍点击这里,使用demo看这里. 我个人的第一个样例也 ...

  5. Javassist 字节码 简介 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  6. TabLayout ViewPager Fragment 简介 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  7. Google Protocol Buffers介绍

    简要介绍和总结protobuf的一些关键点,从我之前做的ppt里摘录而成,希望能节省protobuf初学者的入门时间.这是一个简单的Demo. Protobuf 简介 Protobuf全称Google ...

  8. Hadoop基础-Protocol Buffers串行化与反串行化

    Hadoop基础-Protocol Buffers串行化与反串行化 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们之前学习过很多种序列化文件格式,比如python中的pickl ...

  9. Protocol Buffers官方文档(开发指南)

    本文是对官方文档的翻译,然后截取了一篇非常优秀的文章片段来帮助理解,本人英文水平有限,基本都是直译,如果有不理解的地方请参考英文官方文档,参考的文章链接在文章末尾 protocol buffers简介 ...

随机推荐

  1. python 将字符串转换成字典dict的各种方式总结

    1)利用eval可以将字典格式的字符串与字典户转 >>>mstr = '{"name":"yct","age":10}' ...

  2. Django-Filter源码解析一

    Django Filter源码解析 最近在看Django-FIlter项目的源码,学习一下别人的开发思想: 整体介绍 首先,我从其中一个测试用例作为入口,开始了debug之路,一点一点的断点,分析它的 ...

  3. 15、Redis的集群

     写在前面的话:读书破万卷,编码如有神 -------------------------------------------------------------------------------- ...

  4. 【汇总】PHP-FPM 配置优化(转)

    -----------------------开启php-fpm慢脚本日志 request_slowlog_timeout = 30sslowlog = /usr/local/php/var/log/ ...

  5. MongoDB中的变更通知

    MongoDb 3.6中引入了一个新特性change stream,简单的来说就是变更通知,它提供了一个接口允许应用实时获取数据库变更,这个在ETL.数据同步.数据迁移.消息通知等方面非常有用. 使用 ...

  6. DMA : Timer Trigger Memory-to-memory mode,

    The DMA channels can also work without being triggered by a request from a peripheral. This mode is ...

  7. 怎么发现RAC环境中&#39;library cache pin&#39;等待事件的堵塞者(Blocker)?

    怎么发现RAC环境中的'library cache pin'等待事件的堵塞者(Blocker) 參考自 How to Find the Blocker of the 'library cache pi ...

  8. leetcode第一刷_Unique Binary Search Trees

    这道题事实上跟二叉搜索树没有什么关系,给定n个节点,让你求有多少棵二叉树也是全然一样的做法.思想是什么呢,给定一个节点数x.求f(x),f(x)跟什么有关系呢,当然是跟他的左右子树都有关系.所以能够利 ...

  9. [Asp.net core]使用Polly网络请求异常重试

    摘要 在网络传输过程中,不能保证所有的请求都能正确的被服务端接受或者处理,那么进行简单的重试可以进行简单的补救.比如现在大部分支付功能,在支付成功之后,需要回调我们网站的接口,并且要求我们的接口给一个 ...

  10. Android 开机画面和wallpaper总结

    Android 开机画面和wallpaper总结  1 kernel的开机画面修改 1.图片需求:图片格式:png图片大小:1024x600(具体示lcd分辨率而定). 2.转换图片png图片. 假设 ...