protobuf(Protocol Buffer)是Google推出的一种与语言无关、平台无关、可扩展的序列化结构数据的方法。在兼容性和传输效率上优于Json和Xml。
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
}
apply plugin: 'com.google.protobuf'
protobuf {
protoc {
// You still need protoc like in the non-Android case
artifact = 'com.google.protobuf:protoc:3.6.1'
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {}
}
}
}
}
android {
sourceSets {
main {
proto {
srcDir 'src/main/proto' //proto文件所在路径
include '**/*.proto'
}
java {
srcDir 'src/main/java'
}
}
}
}
dependencies {
compile 'com.google.protobuf:protobuf-java:3.6.1'
compile 'com.google.protobuf:protoc:3.1.0'
}
-keep class com.pl.longlink.** {*;} //protobuf生成类的路径
-keep class com.google.protobuf.Any {*;} //如果要用到Any,需要keep
.proto文件语法规则
(1).syntax:指定proto版本
eg: syntax = “proto3”;
(2).package:默认生成文件的包名
eg: package com.leeduo.protobuf;
(3).option java_outer_classname:指定生成类的类名
eg: option java_outer_classname = “Person”;
(4).option java_package:指定生成文件的包名
eg: option java_package = “com.example.leeduo”;
(5).message:指定可以创建实例的类
(6).类内变量的格式:[修饰符] 类型 变量名 = 数字 [ [default = ] ] [ [packed= true] ];
a).修饰符:
optional:表示修饰的变量可以有也可以没有,proto3移除
required:表示修饰的变量必须有,proto3移除
repeated:表示修饰的变量可以重复,可以表示数组
b).default:为变量设置默认值,使用要带中括号,proto3移除
c).packed:当repeated存在时的选项,可以改变repeated修饰的变量的序列化传输方式。
eg: string name = 1 [default = “jack”];
d).类型:
e).数字:该数字为每一个变量(key)在同一个局域下唯一的标识(tag),大于等于1,0留给未初始化的时候用。
1.编写.proto文件
2.编译项目,生成中间类
3.调用中间类的方法,生成消息对象
4.序列化:writeTo(); 反序列化:parseFrom();
说明:使用提供的builder构建对象
正常情况,使用set方法填充数据
对于被repeated修饰的变量,使用add方法添加
在.proto文件中创建枚举类时,tag要从0开始,其他情况。从1开始。
Protocol Buffer将消息里的每个字段进行编码后,再利用T - L - V 存储方式进行数据的存储,最终得到的是一个二进制字节流
序列化 = 编码 + 存储
Protocol Buffer对于不同数据类型采用不同的序列化方式
Tag - Length - Value,标识 - 长度 - 字段值
(1).Tag:字段标识符,由字段的数据类型(wire_type)和标识号(field_number)构成
Tag的计算公式:Tag = (field_number << 3) | wire_type
通常Tag占用一个字节的长度,如果标识号超过了16,则占用多一个字节的位置
(2).Length:Value的字节长度采用Varint编码
(3).Value:编码后的消息字段的值
编码后的数据具备固定大小 = 64位(8字节) / 32位(4字节)
两种情况下,都是高位在后,低位在前
取出字节串末7位,在最高位添加1构成一个字节。
如果是最后一次取出,则在最高位添加0构成1个字节。
通过将字节串整体往右移7位,继续从字节串的末尾选取7位,直到取完为止。
将形成的每个字节按序拼接成一个字节串,该字节串就是经过Varint编码后的字节。
a).Varint 编码方式的不足
计算机定义负数的符号位为数字的最高位,采用Varint编码方式表示一个负数,需要5个byte。
b).解决
sint32 / sint64 类型表示负数,通过先采用 Zigzag 编码(将有符号数转换成无符号数),再采用Varint编码,从而用于减少编码后的字节数。
编码:sint32:(n <<1) ^ (n >>31)
sint64:(n <<1) ^ (n >>63)
解码:(n >>> 1) ^ -(n & 1)
string类型采用UTF-8编码
T-V或T-L-V
说明:
repeated修饰的字段
无packed: 以多个 T - V对存储
有packed:以T - L - V - V – V-……方式存储
(1).字段标识号(Field_Number)尽量只使用 1-15,且不要跳动使用,因为Tag里Field_Number是需要占字节空间的。如果Field_Number>16时,Field_Number的编码就会占用2个字节,
那么Tag在编码时也就会占用更多的字节。
(2).若需要使用的字段值出现负数,请使用sint32 / sint64,不要使用int32 / int64因为采用sint32 / sint64数据类型表示负数时,会先采用Zigzag编码再采用Varint编码,从而更加有效压缩数据。