ProtocolBuffer是google 定义的一种数据交换的格式,它独立于语言,独立于平台。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。ProtocolBuffer类似于xml、json,不过它更小、更快、也更简单。
目前使用最广泛的数据传输协议为JSON,JSON是一种轻量级的数据交换格式而且层次和结构比较简单和清晰,这里主要对比一下Protocol Buffer和JSON的对比,给出优势和劣势:
优势
劣势
实际数据对比
Protocol Buffer的使用流程总体可以分为三步,如下图所示:
google推荐在Android项目中使用lite版,lite版本生成的java文件更加轻量,其配置如下:
首先创建一个.proto文件,并且在文件中声明如下内容:
在整个proto文件中数据类型分为基本类型和结构类型,其中结构类型主要为:
下面分别介绍一下不同结构的作用及规定:
message表示一个结构,类似于java中类,一个proto文件中可以声明多个message结构:
message可以引用不同proto文件中的message,只要在proto文件中的最上面声明import即可,如下所示:
enum使用很简单,直接在message中声明enum结构体并且将属性声明为对应的enum即可:
在proto3中,enum第一个值必须为0,主要是为了和基础类型的默认值保持一致
map是proto3新加的,使用也很简单:
如下
repeated修饰的属性类似于jsonArray,也类似于java中的List,该修饰符在格式正确的消息中可以重复任意次(包括0次)
日常开发过程中,由于需求的变更,往往需要增加字段,这就涉及到字段的扩充,字段扩充需要达到一个目的: 兼容
所以Protocol Buffer在字段扩充中定义了如下规则:
只要记住上述规则,就能完成字段扩充且老版本也能兼容
Protocol Buffer 更快更小的主要原因如下:
上面这个例子中,在序列化时,"name" 、"count"的key值不会参与,由编号1、2代替,这样在反序列化的时候直接通过编号找到对应的key就可以。需要注意的是编号一旦确定就不可以更改,服务端和客户端通过proto通信的时候需要提前定义号数据格式。
其中Length不一定有,依据Tag确定,例如int类型的数据就只有Tag-Value,string类型的数据就必须是Tag-Length-Value。
Protocol Buffer定义了如下的数据类型,其中部分数据类型已经不再使用:
上面已经介绍了Protocol Buffer的数据结构及Tag的类型,但是Tag块并不是只表示数据类型,其中数据编号也在Tag块中,Tag的生成规则如下:
其中Tag块的后3位表示数据类型,其他位表示数据编号
Java中整数类型的长度都是确定的,如int类型的长度为4个字节,可表示的整数范围为-2 31——2 31-1,但是实际开发中用到的数字均比较小,会造成字节浪费,可变长度编码就能很好的解决这个问题,可变长度编码规则如下:
举个例子:
其中第一个字节由于最高位为1,则后面的字节也是前面的数据的一部分,第二个字节最高位为0,则表示数据计算终止,由于Protocol Buffer是低位在前,整体的转换过程如下:
10000001 00000011 ——> 00000110000001 表示的10进制数为:2^0 + 2^7 + 2^8 = 385 通过上面的例子可以知道一个字节表示的数的范围0-128,上面介绍的Tag生成算法中由于后3位表示数据类型,所以Tag中1-15编号只占用1个字节,所以确保编号中1-15为常用的,减少数据大小。
可变长度编码唯一的缺点就是当数很大的时候int32需要占用5个字节,但是从统计学角度来说,一般不会有这么大的数.
上面介绍了Protocol Buffer的原理,现在通过实例来展示分析过程,我们定义的proto文件如下:
其序列化后的字节数据如下:
前面介绍过Protocol Buffer的 数据结构为TLV,其中L不是必须的,根据T的类型来确定 先看下第一个字节:
这里字节最高位为0,所以该Tag就用这一个字节表示,其中后3位表示类型,前面表示字段编号,所以:
这里字节最高位为0,所以该Tag就用这一个字节表示,其中后3位表示类型,前面表示字段编号,所以: file_num = 0001 = 1 type = 010 = 2 上面介绍过type=2,则后面有Length,按照可变长度编码规则,知道表示长度的字节为:
所以Length=4,则value的长度是4个字节,直接取出后面4个字节:
这4个字节对应的就是test 再看下一组:
由上面的Tag知道: file_num=2 type=0 前面介绍过type=0,后面没有Length,直接就是value,
value=1,通过上面的解析可以知道
上面介绍了Protocol Buffer的原理,解释了为什么Protocol Buffer更快,更小,这里再总结一下:
参考资料:
proto3官网指南:
protobuf-gradle-plugin: 博客: