Protobuf全称是Google Protocol Buffer,是一种高效轻便的结构化数据存储方式,可用于(数据)通信协议、数据存储等。
Xml、Json是目前常用的数据交换格式,它们直接使用字段名称维护序列化后类实例中字段与数据之间的映射关系,一般用字符串的形式保存在序列化后的字节流中。消息和消息的定义相对独立,可读性较好。但序列化后的数据字节很大,序列化和反序列化的时间较长,数据传输效率不高。
xml、json是用字段名称来确定类实例中字段之间的独立性,所以序列化后的数据多了很多描述信息,增加了序列化后的字节序列的容量。
Protobuf和Xml、Json序列化的方式不同,采用了二进制字节的序列化方式,序列化与反序列化不需要解析相应的节点属性和多余的描述信息,用字段索引和字段类型通过算法计算得到字段之前的关系映射,从而达到更高的时间效率和空间效率,特别适合对数据大小和传输速率比较敏感的场合使用。
结论
protobuf是由字段索引(fieldIndex)与数据类型(type)计算(fieldIndex<<3|type)得出的key维护字段之间的映射且只占一个字节,所以相比json与xml文件,protobuf的序列化字节没有过多的key与描述符信息,所以占用空间要小很多。直接是2进数据序列化与反序列化不需要解析相应的节点属性和多余的描述信息所以时间效率要高。
优势
与语言无关,平台无关
Protobuf支持Java, C++, Python等多种语言,支持多个平台。
高效
比XML,Json更小(3~10倍),更快(20 ~ 100倍),更为简单。
扩展性,兼容性好
你可以更新数据结构,而不影响和破坏原有的旧程序。
缺点
二进制格式导致可读性差(二进制格式)
缺乏自描述
序列化反序列化:
序列化:在传递和保存对象时.保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
反序列化:根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。
作用:将数据结构或对象转换成能够被存储和传输(例如网络传输)的格式,同时应当要保证这个序列化结果在之后(可能是另一个计算环境中)能够被重建回原来的数据结构或对象。
protobuf 数据类型
repeated 数组/集合 repeated User users = 1
Protobuf 使用
message Person {
required string name = 1;
required int32 id = 2; [default = 0]
optional string email = 3;
repeated int32 samples = 4
}
其中Person是message这种结构的名称,name、id、email是其中的Field,每个Field保存着一种数据类型,后面的1、2、3是Filed对应的数字id。id在115之间编码只需要占一个字节,包括Filed数据类型和Filed对应数字id,在162047之间编码需要占两个字节,所以最常用的数据对应id要尽量小一些。
Field最前面的required,optional,repeated是这个Filed的规则,分别表示该数据结构中这个Filed有且只有1个,可以是0个或1个,可以是0个或任意个。optional后面可以加default默认值,如果不加,数据类型的默认为0,字符串类型的默认为空串。repeated后面加[packed=true]会使用新的更高效的编码方式。
引用其它message类在同一个文件中,可以直接引用定义过的message类型。在同一个项目中,可以用import来导入其它message类
message 类型都有一个或多个具有唯一编号的字段,每个字段都有一个名称和一个值类型,其中值类型可以是数字(整数或浮点数),布尔值,字符串,原始字节,甚至(如上例所示)其它 protocol buffer message 类型
message具有的字段,形式为:
message xxx {
// 字段规则:required -> 字段只能也必须出现 1 次
// 字段规则:optional -> 字段可出现 0 次或多次
// 字段规则:repeated -> 字段可出现任意多次(包括 0)
// 类型:int32、int64、sint32、sint64、string、32-bit ....
// 字段编号:0 ~ 536870911(2 28-1)(除去 19000 到 19999 之间的数字)
字段规则 类型 名称 = 字段编号;
}
import "myproject/other_protos.proto";
extend Person {
optional int32 bar = 126;
}
一旦定义了 messages,就可以在 .proto 文件上运行 protocol buffer 编译器来生成指定语言的数据访问类。这些类为每个字段提供了简单的访问器(如 name()和 set_name()),以及将整个结构序列化为原始字节和解析原始字节的方法 - 例如,选择的语言是 Java,则运行编译器上面的例子将生成一个名为 Person 的类。然后,你可以在应用程序中使用此类来填充,序列化和检索 Person 的 messages。于是你可以写一些这样的代码:
person = Person.newBuilder() ;
person.set_name("John Doe");
person.set_id(1234);
person.set_email("[email protected]");
你的 .proto 文件将生成什么?
当你在 .proto 上运行 protocol buffer 编译器时,编译器将会生成所需语言的代码,这些代码可以操作文件中描述的 message 类型,包括获取和设置字段值、将 message 序列化为输出流、以及从输入流中解析出 message。
对于 C++,编译器从每个 .proto 生成一个 .h 和 .cc 文件,其中包含文件中描述的每种 message 类型对应的类。
对于 Java,编译器为每个 message 类型生成一个 .java 文件(类),以及用于创建 message 类实例的特殊 Builder 类。
Python 有点不同 - Python 编译器生成一个模块,其中包含 .proto 中每种 message 类型的静态描述符,然后与元类一起使用以创建必要的 Python 数据访问类。
对于 Go,编译器会生成一个 .pb.go 文件,其中包含对应每种 message 类型的类型。
1、在project的build.gradle中配置
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.2'
}
2、在app的build.gradle中配置
apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'
android {
sourceSets {
main {
proto {
//main目录新建proto目录
srcDir 'src/main/proto'
include '**/*.proto'
}
java {
srcDir 'src/main/java'
}
}
}
}
//构建task
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.1.0'
}
generateProtoTasks {
all().each { task ->
task.builtins {
remove java
}
task.builtins {
java {}
// Add cpp output without any option.
// DO NOT omit the braces if you want this builtin to be added.
cpp {}
}
}
}
//生成目录
generatedFilesBaseDir = "$projectDir/src/generated"
}
dependencies {
compile 'com.google.protobuf:protobuf-java:3.1.0'
compile 'com.google.protobuf:protoc:3.1.0'
}
其他语言
使用Protobuf前首先需要准备protobuf需要的Jar包以及在windows下的可执行文件(protoc.exe)通过protoc生成对应文件使用。
例如:
在命令行中定位到protoc.exe所在文件夹,执行“protoc --java_out ./ *.proto”命令
在Android中调用
1.将protobuf需要的Jar包导入到项目中
2.将生成的java文件拷贝到项目中对应的包下,就可以直接调用了
ProtoInfo info= ProtoInfo.newBuilder().setId(1001).setName(“jack”).build();