最近需要对接一个第三方的广告投放接口,感觉挺简单的是,但是当看到技术文档是有点懵,其中提供了一个ts_ui_api.proto文件为api接口文件,content-type 需要为“x-protobuf”,也没有样例。看到这里你可能和我一样protobuf是个什么东西,是的我就是带着这个问题去查阅了很多资料才明白protobuf是什么东西,需求搞定了就来写篇文章记录一下(本文主要为java使用),以防以后使用:
protobuf,全称:Google Protocol Buffer,是Google开源的一种轻便高效的结构化数据存储格式,可以用于结构化数据的串行化,也称作序列化,主要用于数据存储或是RPC数据交换,支持多语言(目前支持C++、JAVA、Python,go等),可拓展;
1.protobuf的优势
平台无关,语言无关,可扩展;
提供了友好的动态库,使用简单;
解析速度快,比对应的XML快约20-100倍;
序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10;
生成了更容易在编程中使用的数据访问类
2.protobuf使用
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).标识号:字段的标识号决定在二进制中字段的位置
备注:该方式为直接切换到bin目录执行,可以通过配置环境变量后直接在命令窗口操作(环境变量中包该解压文件的bin目录配置进去即可)
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