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'
}

安装插件(安装完后重启)

2进制安装prometheus protobuf二进制解析_2进制安装prometheus

点击settings-> browsw ...

2进制安装prometheus protobuf二进制解析_java_02

输入 proto 找到plugins

 

2进制安装prometheus protobuf二进制解析_默认值_03

安装插件,安装成功后重启

 

创建proto文件

2进制安装prometheus protobuf二进制解析_java_04

创建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?

2进制安装prometheus protobuf二进制解析_字段_05

数据类型与JAVA对应关系

创建完文件后记得执行如下操作

2进制安装prometheus protobuf二进制解析_字段_06

创建对象

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