Protocol Buffers谷歌的数据交换格式。
(1)概况
协议缓冲区是Google用于序列化结构化数据的语言无关,跨平台,可扩展的机制。我们可以在github上查看文档。
https://github.com/protocolbuffers/protobuf
还有很多其他的开源工具(thrift,json,xml等等)可方便的处理POJO对象的序列化。
(2)协议编译器安装
协议编译器是用C ++编写的。如果您使用的是C ++,请按照C ++安装说明一起安装protoc和C ++运行时。
对于非C ++用户,安装协议编译器的最简单方法是从我们的发布页面下载预构建的二进制文件:
https://github.com/protocolbuffers/protobuf/releases
在每个版本的下载部分,您可以在zip包中找到预构建的二进制文件:protoc- $ VERSION- $ PLATFORM.zip。它包含protoc二进制文件以及与protobuf一起分发的一组标准.proto文件。
如果您要查找发布页面中未提供的旧版本,请在此处查看maven repo:
https://repo1.maven.org/maven2/com/google/protobuf/protoc/
这些预先构建的二进制文件仅用于已发布的版本。如果你想在HEAD使用github主版本,或者你需要修改protobuf代码,或者你正在使用C ++,建议你从源代码构建自己的protoc二进制文件。
(3)优点
- 产品非常成熟
- 跨语言,不局限java
- 编码后消息很小,利于存储和传输
- 编码性能高
- 支持不同版本的协议前后兼容
- 支持定义可选和必选字段
(4)环境配置
这里环境配置可以查看github中的最新插件配置
https://github.com/google/protobuf-gradle-plugin
这里一定要按照github文档配置,否则如果不是最新的插件很有可能就会配置失败。
【第一步】 配置插件
在项目的根目录的build.gradle文件里添加插件
buildscript {
repositories {
//...
mavenCentral()
}
dependencies {
//...
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
}
}
allprojects {
repositories {
//...
mavenCentral()
}
}
注意:目前最新的插件版本是0.8.8,在以后,请查看github官网查询最新的插件版本,以防配置失败。
【第二步】 在模块中使用protobuf插件
apply plugin: 'com.android.application'
//protobuf插件
apply plugin: 'com.google.protobuf'
android {
//....
sourceSets {
main {
proto {
// In addition to the default 'src/main/proto'
srcDir 'src/main/proto'
//srcDir 'src/main/protocolbuffers'
// In addition to the default '**/*.proto' (use with caution).
// Using an extension other than 'proto' is NOT recommended,
// because when proto files are published along with class files, we can
// only tell the type of a file from its extension.
include '**/*.proto'
}
java {
//...
}
}
test {
proto {
// In addition to the default 'src/test/proto'
srcDir 'src/test/protocolbuffers'
}
}
}
}
srcDir为proto文件的根目录,include导入此根目录下的所有后缀名为.proto的所有文件。当我们编译程序时,会编译.proto文件。

【第三步】 在模块中配置protobuf-lite库
android {
//....
}
protobuf {
protoc {
// You still need protoc like in the non-Android case
artifact = 'com.google.protobuf:protoc:3.0.0'
}
plugins {
javalite {
// The codegen for lite comes as a separate artifact
artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
}
}
generateProtoTasks {
all().each { task ->
task.builtins {
// In most cases you don't need the full Java output
// if you use the lite output.
remove java
}
task.plugins {
javalite { }
}
}
}
}
【第四步】 在指定目录下新建.proto文件,并编辑

编辑的内容为
syntax = "proto2";
package com.xxx.ooo.protobuf;
option java_outer_classname = "ProStudent";
//结构体
message ProStudentInfo {
required int64 mId = 1;
required string mName = 2;
required int32 mGender = 3;
}
目前protobuf已经更新到了protobuf3,上面之所以使用protobuf2是因为greendao:3.2.2对protobuf2完美兼容,我想,在greendao以后的版本中,肯定会完美支持protobuf3的。
(5)proto文件编写知识点
protobuf目前主要有两个大版本:proto2 和 proto3。
其中 proto2 支持 Java、Python、 Objective-C、和 C++。
proto3 增加了对Go、JavaNano、Ruby、和 C#的支持。
【知识一】 syntax关键字
声明版本。例如syntax="proto3",如果没有声明,则默认是proto2。
【知识二】 package关键字
声明包名
【知识三】 import关键字
导入包。类似于java,例如:
import "google/protobuf/timestamp.proto";
【知识四】 java_package关键字
指定生成的类应该放在什么Java包名下。如果你没有显式地指定这个值,则它简单地匹配由package声明给出的Java包名。例如:
option java_package = "com.katyusha.aron.demo";
【知识五】 java_outer_classname关键字
定义封装结构体的类名。如果你没有显式地给定java_outer_classname,则将通过把文件名转换为首字母大写来生成。例如:
option java_outer_classname = "ProStudent";
【知识六】 message关键字
类似于java中的class关键字。这里用来定义结构体。
【知识七】 required、optional、repeated关键字
required用于修饰属性,字段只能也必须出现 1 次(proto3中不需要使用)
optional用于修饰属性,字段可出现 0 次或1次(proto3中不需要使用)
repeated用于修饰属性,字段可出现任意多次(包括 0)(其实就是List数组)
【知识八】 基本数据类型
Protobuf定义了一套基本数据类型。几乎都可以映射到C++\Java等语言的基础数据类型。
| protobuf 数据类型 | 描述 | 打包 | C++语言映射 |
|---|---|---|---|
| bool | 布尔类型 | 1字节 | bool |
| double | 64位浮点数 | N | double |
| float | 32为浮点数 | N | float |
| int32 | 32位整数 | N | int |
| uin32 | 无符号32位整数 | N | unsigned int |
| int64 | 64位整数 | N | __int64 |
| uint64 | 64为无符号整 | N | unsigned __int64 |
| sint32 | 32位整数,处理负数效率更高 | N | int32 |
| sing64 | 64位整数 处理负数效率更高 | N | __int64 |
| fixed32 | 32位无符号整数 | 4 | unsigned int32 |
| fixed64 | 64位无符号整数 | 8 | unsigned __int64 |
| sfixed32 | 32位整数、能以更高的效率处理负数 | 4 | unsigned int32 |
| sfixed64 | 64为整数 | 8 | unsigned __int64 |
| string | 只能处理 ASCII字符 | N | std::string |
| bytes | 用于处理多字节的语言字符、如中文 | N | std::string |
| enum | 可以包含一个用户自定义的枚举类型uint32 | N(uint32) | enum |
| message | 可以包含一个用户自定义的消息类型 | N | object of class |
N 表示打包的字节并不是固定。而是根据数据的大小或者长度。
例如int32,如果数值比较小,在0~127时,使用一个字节打包。
关于枚举的打包方式和uint32相同。
关于message,类似于C语言中的结构包含另外一个结构作为数据成员一样。
关于 fixed32 和int32的区别。fixed32的打包效率比int32的效率高,但是使用的空间一般比int32多。因此一个属于时间效率高,一个属于空间效率高。根据项目的实际情况,一般选择fixed32,如果遇到对传输数据量要求比较苛刻的环境,可以选择int32.
【知识九】 枚举
枚举一般展示状态信息
enum StudentType{
Pupil = 0;//小学生
MIDDLE_STUDENT = 1;//中学生
COLLEGE_STUDENT = 2;//大学生
}
【知识十】 内部message
可以通俗的的理解为结构体的内部类。
//结构体
message ProStudentInfo {
//....
message ProStu{
required int64 mId = 1;
required string mName = 2;
}
}
[本章完...]