Protobuf,转换的效率高(耗时短),占内存空间小。可读性差。估计日后在网络传输会占据大片江山。
Protobuf(可以简单理解为json的二进制版)
----------------------------------------------------------------------------
什么是Protobuf
官方文档给出的是:
a language-neutral, platform-neutral, extensible way of serializing structured data for use in communications protocols, data storage, and more.
Protobuf的优点
1,性能好,效率高
2,代码生成机制,数据解析类自动生成
3,支持向后兼容和向前兼容
4,支持多种编程语言(java,c++,python)
5,参考文档:
Protobuf的缺点
1, 应用不够广
2, 二进制格式导致可读性差(二进制格式)
3, 缺乏自描述
官方文档描述如下:for instance, protocol buffers would not be a good way to model a text-based document with markup (e.g. HTML), since you cannot easily interleave structure with text
4, 参考文档:
核心概念
1,.proto文件
相当于确定数据协议,数据结构中存在哪些数据,数据类型是怎么样
2,modifiers
2-1 required 不可以增加或删除的字段,必须初始化
2-2 optional 可选字段,可删除,可以不初始化
2-3 repeated 可重复字段, 对应到java文件里,生成的是List
3,Message
在proto文件里,数据的协议时以Message的形式表现的。
4, Build
生成具体的java类时,例如Person.java,同时会存在build方法。文档的意思是对于转化后的数据,具有唯一性,build提供了便利的方法来初始化这些数据。
在AndroidStudio中的配置
1、在project的build.gradle中配置
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
注意:如果gradle的版本是3以下,plugin版本要用0.8.0版本
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'
}
安装插件(安装完后重启)
点击settings-> browsw ...
输入 proto 找到plugins
安装插件,安装成功后重启
创建proto文件
创建proto包
package tutorial;
option java_package = "cn.tan.customsystem";
option java_outer_classname = "AddressBookProtos";
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
message AddressBook {
//repeated Person person = 1;
map<string, Person> mapList=6;//定义string 连着一串list
}
package在Java里面代表这个文件所在的包名,在c#里面代表该文件的命名空间,message代表一个类,
“required”:表示此字段值必填,一个结构良好的message至少有一个flied为“required”
“optional”:表示此字段值为可选的。对于此类型的字段,可以通过default来指定默认值,这是一个良好的设计习惯。
如果没有指定默认值,在encoding时protobuf将会用一个特殊的默认值来替代。对于string,默认值为空,bool类型默认为false,数字类型默认位0,对于enum则默认值为枚举列表的第一个值。
“repeated”:表示这个字段的值可以允许被重复多次,如果转换成JAVA代码,此filed数据结构为list,有序的。可以在“repeated”类型的filed后使用“packed”--压缩,提高数据传输的效率。
特别需要注意:当你指定一个filed位required时,需要慎重考虑这个filed是否永远都是“必须的”。将一个required调整为optional,需要同时重新部署数据通讯的Client和Server端,否则将会对解析带来问题。
可以在一个.proto文件中,同时声明多个message,这样是允许的。
为message或者filed添加注释,风格和JAVA一样
optional int32 page = 3;// Which page number do we want?
数据类型与JAVA对应关系
创建完文件后记得执行如下操作
创建对象
AddressBookProtos.Person.PhoneNumber phoneNumber1= AddressBookProtos.Person.PhoneNumber.newBuilder()
.setNumber("12345678912")
.build();
AddressBookProtos.Person.PhoneNumber phoneNumber2= AddressBookProtos.Person.PhoneNumber.newBuilder()
.setNumber("21234567891")
.build();
List<AddressBookProtos.Person.PhoneNumber> list =new ArrayList<>();
list.add(phoneNumber1);
list.add(phoneNumber2);
AddressBookProtos.Person person1 = AddressBookProtos.Person.newBuilder()
.setId(1)
.setName("谭123")
.setEmail("123")
.addAllPhone(list)
//.addPhone(phoneNumber1) //增加单个
//.addPhone(phoneNumber2) //增加单个
.build();
List<AddressBookProtos.Person.PhoneNumber> getList=person1.getPhoneList();
AddressBookProtos.Person person2 = AddressBookProtos.Person.newBuilder()
.setId(1)
.setName("谭222")
.setEmail("222")
.build();
Map<String, AddressBookProtos.Person> personMap=new HashMap<>();
personMap.put("num1",person1);
personMap.put("num2",person2);
AddressBookProtos.AddressBook book = AddressBookProtos.AddressBook.newBuilder()
.putAllMapList(personMap)
//.putMapList("num1",person1)
//.putMapList("num2",person2)
.build();
try{
AddressBookProtos.Person person3=AddressBookProtos.Person.parseFrom(person1.toByteArray());
Log.w("tan","person2.getName()="+person2.getName());
}catch (Exception e) {
e.printStackTrace();
}
PS:
Maps
pb中也可以使用map类型(官方并不认为是一种类型,此处称之为类型仅便于理解),绝大多数scalar类型都可以作为key,除了浮点型和bytes,枚举型也不能作为key,value可以是除了map以外的任意类型:
map
类型字段不支持repeated
,value的顺序是不定的。
官方部分的文档请看下文:
https://www.jianshu.com/p/ea656dc9b037