protobuf实现数据交互

    最近需要对接一个第三方的广告投放接口,感觉挺简单的是,但是当看到技术文档是有点懵,其中提供了一个ts_ui_api.proto文件为api接口文件,content-type 需要为“x-protobuf”,也没有样例。看到这里你可能和我一样protobuf是个什么东西,是的我就是带着这个问题去查阅了很多资料才明白protobuf是什么东西,需求搞定了就来写篇文章记录一下(本文主要为java使用),以防以后使用:

Protobuf是什么?

          protobuf,全称:Google Protocol Buffer,是Google开源的一种轻便高效的结构化数据存储格式,可以用于结构化数据的串行化,也称作序列化,主要用于数据存储或是RPC数据交换,支持多语言(目前支持C++、JAVA、Python,go等),可拓展;

1.protobuf的优势

  • 平台无关,语言无关,可扩展;

  • 提供了友好的动态库,使用简单;

  • 解析速度快,比对应的XML快约20-100倍;

  • 序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10;

  • 生成了更容易在编程中使用的数据访问类

2.protobuf使用

  • 根据不同平台有相应的支持库java支持库:地址连接(支持maven,gradle等引用方式)

实际开发

1.定义协议文件xx.proto(如下)

syntax = "proto2";  //protobuf的编译器版本
package cn.lp.box.advertisement; //包名
import "src/help.proto"; //引用的外部文件


enum OsType {
    UNKNOWN = 0;
    ANDROID = 1;  // Android
    IOS = 2;  // iOS
    WINDOWS = 3;
};

//message字段想到与java中的class声明一个类
message Version {
    optional uint32 major = 1[default = 0]; //声明一个major 字段类型的int 默认值为0 字段标号为1
    optional uint32 minor = 2[default = 0]; 
    optional uint32 micro = 3[default = 0]; 
};


//这个是一个枚举类型的数据
enum UdIdType {
    MAC = 1; //声明一个属性值为1
    MEDIA_ID = 2; 
};

message UdId {
    optional UdIdType id_type = 1; //声明一个id_type 字段 类型为UdIdType 字段标号为1
    required id id = 2;  //声明一个id 字段 类型为id 字段标号为2
};


message Size {
    optional uint32 width = 1[default = 0];  
    optional uint32 height = 2[default = 0];  
};

message Device {
    optional UdId udid = 1; //声明一个udid 字段 类型为UdId 字段标号为1 
    optional Version os_version = 2; 
    required string vendor = 3[default = ""]; 
    required string model = 4[default = ""]; 
    optional Size screen_size = 5; //声明一个screen_size 字段 类型为Size 字段标号为5 
}

message TsApiRequest {
   .../字段这里就不写了
}
message TsApiResponse{
   .../字段这里就不写了
}

看看是否感觉很熟悉,是的感觉还java还是不比较像,就是一些关键之被替换了,如class被换成了 message等 

备注:

(1).required,optional 、repeated为protobuf中的特有修饰符,用于修饰字段具体区别如下:

 - required:该值是必须要设置的; 
 - optional :该字段可以有0个或1个值(不超过1个); 
 - repeated:该字段可以重复任意多次(包括0次),类似于C++中的list;

 根据每个修饰符不同,生成的代码与区别的

(2).标识号:字段的标识号决定在二进制中字段的位置

2.文件转换(将xx.proto文件转化为相应的平台语言)

  • 文件转换工具protoc,protoc是proto文件的编译器可以将xx.proto文件编辑为响应的文件:protoc下载地址
  • protoc安装,没有什么安装的其实就是解压(请注意解压路径尽量避免误中文)就可以使用了
  • protoc使用(cmd打开命令窗口操作):
protobuf实现数据交互_第1张图片 A.切换到解压后的bin目录
​​​B:执行的命令
C:编辑的文件格式(cpp_out表示C++代码,--java_out表示Java代码,--python_out表示Python代码,--go_out表示go代码)
D:为输出文件路径(实际文件在改路径+.proto文件的包名路径中)
E;为编辑文件路径(图上在本目录所以只有文件名称)​​​​

备注:该方式为直接切换到bin目录执行,可以通过配置环境变量后直接在命令窗口操作(环境变量中包该解压文件的bin目录配置进去即可)

3.android中使用

   A.添加依赖:

compile 'com.google.protobuf:protobuf-java-util:3.6.1' 具体参考这里(该包中已经引用了Gson引用是避免重复引入导致代码报错,本人已入坑)

B.将转换后的文件按照指定的包结构复制进项目(和.proto文件中的包名路径一直,负责文件会报错)

C.组织数据:

TsUiApi.TsApiRequest.Builder apiRequest = TsUiApi.TsApiRequest.newBuilder();//为请求接口时需要的数据对象

TsUiApi.UdId udId = TsUiApi.UdId.newBuilder().setIdType(TsUiApi.UdIdType.MEDIA_ID).setId("id").build();
TsUiApi.Size screenSize = TsUiApi.Size.newBuilder().setWidth(102).setHeight(56).build();
TsUiApi.Version osVersion = TsUiApi.Version.newBuilder().setMicro(0).setMajor(0).setMinor(0).build();
//获取Device对象
TsUiApi.Device device = TsUiApi.Device.newBuilder().setVendor("data").
                .setUdid(udId)
                .setScreenSize(screenSize)
                .setOsVersion(osVersion)
                .build();

TsUiApi.Version apiVersion = TsUiApi.Version.newBuilder().setMicro(0).setMajor(1).setMinor(0).build();

 apiRequest.setDevice(device);
 apiRequest.setApiVersion(apiVersion)

//通过build方法获取TsApiRequest 对象,tsApiRequest 将会被转化为  byte[] 放入请求体中传递
 TsUiApi.TsApiRequest tsApiRequest = apiRequest.build();


这个对象比较熟悉把,就是刚才.proto文件中的字段

D.发送数据这个根据网络框架不同方式不同,由于旧项目使用的是AsyncHttpClient,修改一下框架的方法才支持:

 //组织数据
        byte[] data = tsApiRequest.toByteArray();

        ByteArrayEntity httpEntity = new ByteArrayEntity(data);
        //数据整体以流的形式传递
        mAsyncHttpClient.post(context.getApplicationContext(), "http://jpaccess.baidu.com/api_6", httpEntity, 
                "x-protobuf", advHandler);

 

E:数据接收呢,还记得TsApiResponse类吗:

byte[] responseBytes

TsUiApi.TsApiResponse tsApiResponse = TsUiApi.TsApiResponse.parseFrom(responseBytes);

同tsApiResponse获取响应的值转为为本地可用的数据;

 

本文就介绍到这里,可能还有许多不足之处,希望对正在学习的您有帮助,如果您有好的建议请感谢您的反馈。

文章参考:https://blog.csdn.net/longxuanzhigu/article/details/81538748

 

你可能感兴趣的:(protobuf,Android)