最近项目中有用到传输数据,用到了Protobuf, 在此总结一下如何使用Protobuf
1. 什么是protobuf
Google 官网的解释是: Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data 。
翻译过来大概就是:Protocol Buffers(也称protobuf)是google旗下一款独立于开发语言,独立于平台的可扩展的结构化数据序列机制。
简单来说,就是像json、xml这种用来传输数据的一种数据交互协议。不过,相比与json、xml,protobuf采用了二进制序列化 从而更加轻便与高效,体积小3倍,处理速度快8~20倍,更适合对传输效率敏感的项目中,而且也是跨平台,支持多种编程语言C,C++,java, python, JavaScript, php, ruby等;
2. 使用protobuf 需要准备的工作
在一个项目中要使用protobuf 需要准备两项内容:
一个是protobuf的jar包,这个是需要加入到我们项目中lib文件夹下的
另外一个就是protobuf根据我们结构化的数据生成的java文件, 这个也是要加到项目中
一 ) build protobuf的jar包
由于ProtoBuf的官方下载包并不包含jar文件,需要自己进行编译,可以选择Linux下编译,也可以选择windows
我这里选择了windows的方法,有兴趣的同学可以试试Linux环境下的
需要准备的东西:JDK,maven,protobuf.exe,protobuf的源文件
1)JDK 下载安装
下载地址: https://www.orace.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
选择 jdk-8u211-windows-x64.exe
下载速度真的是醉了,需要安装包的可以找我,直接安装完 配置环境变量即可 (一般Android开发人员都有安装这个,安装过的可以选择忽略这步)
2)Maven 下载安装
① 下载地址 https://maven.apache.org/download.cgi
将下载的压缩包解压到某个目录下
我这里下载到了 D:\Program Files\apache-maven-3.6.1
② 添加系统环境变量 path
③ 配置Maven本地仓库
找到D:\Program Files\apache-maven-3.6.1\conf\settings.xml
添加 <localRepository>D:/Program Files/apache-maven-3.6.1/repo</localRepository>
并添加repo 这个文件夹,以后maven需要从仓库下载的文件都会下载到这里
3)Protobuf 源码下载
下载地址: https://github.com/protocolbuffers/protobuf/releases
选择 protobuf-java-3.7.1.zip 和 protoc-3.7.1-win64.zip
① 解压 protobuf-java-3.7.zip 到本地某个目录下
我下载到了F:\Project\protobuf\protobuf-3.7.1
添加系统环境变量path F:\Project\protobuf\protobuf-3.7.1\src
② 将解压的protoc-3.7.1-win64.zip 里面的 bin/protoc.exe copy到 F:\Project\protobuf\protobuf-3.7.1\src
可以cmd输入命令 protoc –v 查看是否配置成功
4)编译Protobuf源码 获取 jar 包
打开CMD 切换到F:\Project\protobuf\protobuf-3.7.1\java目录下
运行 mvn install
开始编译
******************************************************************************************
这里遇到了一个搞了半天才搞定编译error:
【No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?】
网上大都是Eclipse内编译出现的问题,所以都是在eclipse里设置里修改(但是我这里并没有用到eclipse)
其实本质问题是 maven的jdk和本地装的jdk版本不一致发生的这个问题,修改如下:
F:\Project\protobuf\protobuf-3.7.1\java\pom.xml
修改上面的文件如下:
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<fork>true</fork>
<executable>C:\Program Files\Java\jdk1.8.0_131\bin\javac.exe</executable>
<source>1.8</source>
<target>1.8</target>
</configuration>
编译完成后可以在F:\Project\protobuf\protobuf-3.7.1\java\core\target目录中找到protobuf-3.7.1.jar文件.
到这里 这是我们的第一个产物,万里长征走了大半~~
二)protobuf根据我们结构化的数据生成的java文件
在AS里建项目之前我们先添加protobuf组件到AS
File->settings->plugins->Browse repositories 搜索Protobuf Support
点击安装很快完成,这个是检验protobuf 语法的
在AS里新建一个空的project
1)首先,我们需要在全局的buid.gradle文件中添加上protobuf工具插件:
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.6'
2)然后,要app/buid.gradle文件中:
①添加上此插件
apply plugin: 'com.google.protobuf'
②添加依赖
implementation 'com.google.protobuf:protobuf-java:3.7.1'
③添加protobuf 配置
protobuf {
//配置protoc编译器
protoc {
artifact = 'com.google.protobuf:protoc:3.7.1'
}
//这里配置生成目录,编译后会在build的目录下生成对应的java文件
generateProtoTasks {
all().each { task ->
task.builtins {
remove java
}
task.builtins {
java {}
}
}
}
}
④ 在Android添加 protobuf文件的目录
sourceSets {
main {
proto {
srcDir 'src/main/java/com/gwrein/protobuftest'
}
}
}
3)在src/main/java/com/gwrein/protobuftest目录下添加文件TouchActionProto.proto,添加如下代码
syntax = "proto3";
message TouchActionProto{
int32 action = 1;
int32 x = 2;
int32 y = 3;
}
Protobuf3 相较于以前版本 语法格式有一定的变化 中文翻译文档参考
https://blog.csdn.net/u011518120/article/details/54604615
4) 在AS buid项目 会将.proto 文件生成java文件
可以在\app\build\generated\source\proto\debug\java\ 目录下找到
这些生成的java文件封装好了一些API序列化和反序列化 ,这些API 可以直接在项目中调用
3 项目中使用Protobuf
之前有见到百度CarLife 车机端源码中有用到过Protobuf, 这里以百度carlife的源码 介绍一下怎么使用protobuf
百度 Carlife 车机端 源码地址 https://github.com/ApolloAuto/apollo-DuerOS
1)把我们的产物jar文件添加到libs 文件夹下
百度carlife用的是老版本,但是不影响对本文的理解。
但是需要注意车机端和手机端 引用的protobuf的版本需要保持一致
2)添加 lib目录到app 的build.gradle里
dependencies {
compile fileTree(include: ['*.jar'], exclude: ['android-support-v4.jar'], dir: 'libs')
compile 'com.android.support:support-v4:22.2.0'
}
3)将protobuf生成的java文件添加到项目中
这里有很多这种文件都放到了一起,注意这些文件都是自动生成的,不要手动修改,要修改的话去前面的项目里修改
4)Protobuf 的使用
以下是车机端代码,将touch数据序列化(toByteArray)成字节数组 发送出去
import com.baidu.carlife.protobuf.CarlifeTouchActionProto.CarlifeTouchAction;
public void sendAction(float x, float y, int action) {
try {
String mInfo = null;
int tx = (int) (x * mPhoneContainerWidth / mContainerWidth);
int ty = (int) (y * mPhoneContainerHeight / mContainerHeight);
mInfo = "x = " + tx + " | y = " + ty + " | action = " + action;
LogUtil.i(TAG, "sendActionEvent: " + mInfo);
CarlifeCmdMessage command = new CarlifeCmdMessage(true);
command.setServiceType(CommonParams.MSG_TOUCH_ACTION);
CarlifeTouchAction.Builder builder = CarlifeTouchAction.newBuilder();
//将结构体里的成员赋值
builder.setX(tx);
builder.setY(ty);
builder.setAction(action);
CarlifeTouchAction actionInfo = builder.build();
//将actionInfo 对象序列化 然后打包到command里 发送出去
command.setData(actionInfo.toByteArray());
command.setLength(actionInfo.getSerializedSize());
ConnectManager.getInstance().writeCarlifeTouchMessage(command);
} catch (Exception ex) {
ex.printStackTrace();
}
}
由于没有百度carlife 手机端代码 我这里进行了简单的code 用于说明 如何将接收到数据反序列化(解析成对象)
case CommonParams.MSG_TOUCH_ACTION:
carlifeMsg = (CarlifeCmdMessage) msg.obj;
CarlifeTouchAction touchAction= null;
try {
// 将序列化后的数据(字节型数组)解析成对象
touchAction = CarlifeTouchAction.parseFrom(carlifeMsg.getData());
} catch (InvalidProtocolBufferException e) {
Log.e(TAG, "Getr Touch Action Info Error");
e.printStackTrace();
break;
}
//获取解析后结构数据
int action = touchAction.getAction();
int x = touchAction.getX();
int y = touchAction.getY();
MotionEvent event = null;
switch (action) {
case MotionEvent.ACTION_DOWN:
downTime = SystemClock.uptimeMillis();
break;
}
// 根据获取额数据 生成motionEvent
event = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(),action, x, y, 0);
//将motionEvent 注入(这个事件只会分发到此应用中)
mInstrumentation.sendPointerSync(event);
break;
protobuf的使用 先总结这么多,也许有理解或总结不到位的地方,欢迎各路大神指出