带你玩转ProtocolBuffers

1. Protocol Buffers概念与优势

Protocol Buffers是Google公司开发的一种数据描述语言,类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面,但更小更快更简单

它不依赖于语言和平台并且可扩展性极强。google 为其提供了多种语言的实现:java、c#、c++、Objective-C、JavaScript、PHP、Dart、Ruby、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。

这里写图片描述

2. 下载并编译安装Protocol Buffers

  1. 下载开发包:

    https://github.com/google/protobuf/releases
  2. 进入该目录,执行autogen.sh,该命令用于编译配置的 configure 等文件:

    $ cd protobuf
    $ ./autogen.sh

    上面的操作过程中,如果你还缺少安装一些命令libtoolautoconf,可以通过如下命令安装:

    $ brew install libtool
    $ brew install automake
  3. 查看configure 命令是否安装成功:

    $ ./configure --help
  4. 执行configure对编译进行配置:

    $ ./configure 
  5. 当生成了makefile文件,编译并安装:

    $ make
    $ sudo make install
  6. 上面的安装过程需要执行10多分钟,请耐性等待。安装之后执行如下命令以确认已经装好:

    $ protoc --version
    libprotoc 3.5.1

    如果是windows平台,只需要下载即可protoc-3.5.1-win32版本,并且需要配置path环境变量.

3. 创建 .proto 文件定义传输数据格式

.proto 文件中的定义很简单:为每个想要序列化的数据结构添加一个 消息(message) ,然后为消息中的每个字段指定一个名字和类型(如下基本类型)以及一个tag数字。

Java Type .proto Type
double double
float float
int int32 uint32 sint32
long int64 uint64 sint64
boolean bool
String string
ByteString bytes

1. 如下为我们创建的传输格式文件shopcar.proto

//定义传输数据的协议格式文件语法版本,目前主流版本有proto2 proto3
syntax = "proto3";

//这用于防止不同项目间的命名冲突。在Java中,包名被用作Java包(前提没声明java_package)
package com.a520it.protobuffersdemo.bean;

//生成的类应该放在什么Java包名下。如果你没有显式地指定这个值,则它简单地匹配由package 声明给出的Java包名
option java_package="com.a520it.protobuffersdemo.bean";
//定义应该包含这个文件中所有类的类名.如果你没有显式地给定java_outer_classname ,
//则将通过把文件名转换为首字母大写来生成。比如"my_proto.proto",默认情况下,将使用 "MyProto" 做为它的外层类的类名。
option java_outer_classname = "ShopcarProtos";

//消息只是包含了具有类型的字段的聚合。
message Store{
    //required:声明该值是必要的值,不能为空(proto3舍弃)
    //optional:声明该值是可选的值,可以为空(proto3舍弃)
    //proto3舍弃以上两种修饰,值都能为空
    //repeated:声明该值为多个数值,可以组成数组的形式

    //许多标准数据类型可用作字段类型,包括bool,int32,float,double,和string。
    //每个元素上的 " = 1"," = 2"标记标识在二进制编码中使用的该字段唯一的 "tag" 。
    // 1~15 :在编码上少一个字节,将那些数字用作常用的或重复的元素的tag
    // 16~ :留给更加不常用的可选元素
    string name = 1;
}

message Product{

     int64 id = 1;
     string name = 2;
     double price = 3;
     int32 buyCount = 4;
     Store store = 5;
}

message Shopcar{
    repeated Product products = 1;
}
  1. 进入下载好的protocal buffer文件夹目录,可以通过如下命令编译 .proto 文件:
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/shopcar.proto

-I,–java_out 分别用于指定源目录和目的目录 (放置生成的代码的位置;通常与目的相同),最后的参数为 .proto 文件的路径,下面是我自己的实际配置:

$ protoc -I=/Users/lean/Desktop/ProtoBuffersDemo/app/src/main/java --java_out=/Users/lean/Desktop/ProtoBuffersDemo/app/src/main/java /Users/lean/Desktop/ProtoBuffersDemo/app/src/main/java/shopcar.proto
  1. 将生成的代码放到我们项目中,打开文件,发现出现了未识别类的错误。添加依赖:
compile 'com.google.protobuf:protobuf-java:3.5.1'

4.生成发送数据流

public void test1(View v) throws Exception {
    ShopcarProtos.Shopcar.Builder builder = ShopcarProtos.Shopcar.newBuilder();
    for (int i = 0; i < 10; i++) {
        ShopcarProtos.Store store = ShopcarProtos.Store.newBuilder()
                .setName("奇趣旗舰店")
                .build();
        ShopcarProtos.Product product = ShopcarProtos.Product.newBuilder()
                .setId(i)
                .setBuyCount(10 + i)
                .setName("奇趣巧克力")
                .setPrice(18.8d)
                .setStore(store)
                .build();
        builder.addProducts(i, product);
    }
    ShopcarProtos.Shopcar shopcar = builder.build();
    shopcar.writeTo(openFileOutput("shopcar.txt", MODE_PRIVATE));
}

5.解码数据流

public void test2(View v) throws Exception {
    ShopcarProtos.Shopcar shopcar = ShopcarProtos.Shopcar.parseFrom(openFileInput("shopcar.txt"));
    int productsCount = shopcar.getProductsCount();
    for (int i = 0; i < productsCount; i++) {
        ShopcarProtos.Product product = shopcar.getProducts(i);
        Log.v("IT520", "*************Product************");
        Log.v("IT520", product.getId()+"");
        Log.v("IT520", product.getName()+"");
        Log.v("IT520", product.getPrice()+"");
        Log.v("IT520", product.getBuyCount()+"");
        Log.v("IT520", product.getStore().getName()+"");
    }
}

6. 集成自动解析.proto文件插件

  1. 找到项目的build.gradle文件,添加如下代码:

    classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
  2. 找到模块的build.gradle文件,添加如下代码:

    ...
    apply plugin: 'com.google.protobuf'
    
    android {
       ...
       sourceSets {
           main {
               java {
                   srcDir 'src/main/java'
               }
               proto {
                   srcDir 'src/main/proto' //指定.proto文件路径
               }
           }
       }
    }
    
    protobuf {
       protoc {
           artifact = 'com.google.protobuf:protoc:3.5.1' // 也可以配置本地编译器路径
       }
       generateProtoTasks {
           all().each { task ->
               task.builtins {
                   remove java
               }
               task.builtins {
                   java {}// 生产java源码
               }
           }
       }
    }
    
    dependencies {
       ...
       //解析.proto文件的在线工具protoc
       compile 'com.google.protobuf:protoc:3.5.1'
    }
    
  3. 在文件的这个位置定义.proto文件:

这里写图片描述

syntax="proto3";

package com.a520it.protobuffersdemo.proto;

option java_package="com.a520it.protobuffersdemo.bean";
option java_outer_classname="OrderInfoProtos";

message Product{
    int64 pid = 1;
    string name = 2;
    double price = 3;
    int64 buyCount = 4;
}

message OrderInfo{
    int64 oid = 1;
    string name = 2;
    double totalPrice = 3;
    repeated Product products= 4;
}
  1. 当build成功后,就会在如下地址产生java文件:

    这里写图片描述

  2. 编码解码数据:

    public void test3(View v) throws Exception {
           OrderInfoProtos.OrderInfo.Builder infoBuilder = OrderInfoProtos.OrderInfo.newBuilder();
           for (int i = 0; i < 6; i++) {
               OrderInfoProtos.Product product = OrderInfoProtos.Product.newBuilder()
                       .setPid(i + 1)
                       .setName("德芙巧克力")
                       .setBuyCount(4)
                       .setPrice(18.8d)
                       .build();
               infoBuilder.setOid(1001)
                       .setName("奇趣巧克力高级订单")
                       .setTotalPrice(10008.00)
                       .addProducts(i, product);
           }
           OrderInfoProtos.OrderInfo orderInfo = infoBuilder.build();
           orderInfo.writeTo(openFileOutput("orderinfo.txt", MODE_PRIVATE));
       }
    
       public void test4(View v) throws Exception {
           OrderInfoProtos.OrderInfo orderInfo = OrderInfoProtos.OrderInfo.parseFrom(openFileInput("orderinfo.txt"));
           Log.v("IT520", "*************OrderInfo************");
           Log.v("IT520", "orderInfo.getOid()" + orderInfo.getOid());
           Log.v("IT520", "orderInfo.getName()" + orderInfo.getName());
           Log.v("IT520", "orderInfo.getTotalPrice()" + orderInfo.getTotalPrice());
           int productsCount = orderInfo.getProductsCount();
           for (int i = 0; i < productsCount; i++) {
               OrderInfoProtos.Product product = orderInfo.getProducts(i);
               Log.v("IT520", "*************Product:"+i+"************");
               Log.v("IT520", "orderInfo.getPid()" + product.getPid());
               Log.v("IT520", "orderInfo.getName()" + product.getName());
               Log.v("IT520", "orderInfo.getBuyCount()" + product.getBuyCount());
               Log.v("IT520", "orderInfo.getPrice()" + product.getPrice());
           }
       }

猜你喜欢

转载自blog.csdn.net/qq285016127/article/details/79906220