目录:
简介
简单使用
—— 1、编写.proto 文件
—— 2、编译proto文件成目标文件(本文为java)
—— 3、将目标文件导入项目
—— 4、将依赖包导入项目
———— 自动依赖
———— 手动依赖
Maven项目
—— 创建Maven项目
—— 安装ProtoBuf插件
—— 编译proto文件
—— Java
—— Web
Android
—— 插件
—— 依赖
—— 编码
—— 结果
简介
常见的序列化协议有xml、json以及本文将介绍的protobuf。
ProtoBuf由Google出品,由其二进制格式,所以在体积和传输方面性能优越,但也因此可读性差。
简单使用
流程:
1、编写.proto 文件
2、编译proto文件成目标文件(本文为java)
3、将目标文件导入项目
4、将依赖包导入项目
1、编写.proto 文件
.proto 文件用来定义消息类型,可理解为java中的类
syntax = "proto3";//使用proto3的语法,不写默认为proto2
message SearchRequest {
string query = 1;//数字为该字段在二进制文件中的标识(字段号),使用过一次后不应该被改变。字段号1-15占一个字节16-2047占两个字节,常用字段字段号最好用一个字节。19000-19999为保留字段号不可用
int32 page_number = 2;
int32 result_per_page = 3;
}
message SearchResponse {//一个.proto文件可以定义多个message
...
}
复杂点的
syntax = "proto2";
package tutorial;//如果没有java_package则使用此package,最好不要为空因为不同语言都会使用,java_package只java使用
option java_package = "com.example.tutorial";//生成java文件的包
option java_outer_classname = "AddressBookProtos";//生成java文件的名字
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 phones = 4;//java中的list
}
message AddressBook {
repeated Person people = 1;
}
官方参考: https://developers.google.cn/protocol-buffers/docs/proto3#simple
2、编译proto文件成目标文件(本文为java)
编译前需下载protobuf的编译器,也可以下载源码。下载地址
我的机器是Mac所以下载了第一项
解压后
安装protobuf
进入到刚刚的解压目录
$ ./configure
$ make
$ make check
$ make install
成功后
编译proto文件三个参数
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto
$SRC_DIR:proto文件路径
$DST_DIR:生成java文件的路径
3、将目标文件导入项目
复制粘贴的工作,将生成的java文件连同包结构一起移到项目。如果包名与项目的包名相同则合并,不相同则在同级。
4、将依赖包导入项目
自动依赖
如果项目使用Maven或Gradle构建的,可以直接添加依赖。
Maven
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.5.1</version>
</dependency>
Gradle
implementation 'com.google.protobuf:protobuf-java:3.5.1'
若不是的话,需要找jar包放入项目。哪里来的jar包呢??自己打。打包的话需要用到Maven,顺便写下安装流程
手动依赖
安装Maven
解压后重命名放在Library下
接着配置环境变量
touch ~/.bash_profile
vi ~/.bash_profile
具体配置:
# maven所在的目录
export M2_HOME=/Users/mr.lin/Library/Maven
# maven bin所在的目录
export M2=$M2_HOME/bin
# 将maven bin加到PATH变量中
export PATH=$M2:$PATH
环境变量刷新
source ~/.bash_profile
最后
打包
源码已经有了,Maven也配置好了
copy一个protoc文件(上述mac安装protobuf时bin目录下的文件)到src目录下
再copy一个protoc文件到java/core/src/目录下:
然后回到java目录,执行:
mvn package
如果中途失败请注意,是否在此路径下生成过文件
protobuf-3.5.1/java/core/src/main/java/com/google/protobuf
我就失败过,将生成的文件删除重新执行即可。但失败原因不止这一个具体还要看错误信息。
最终结果
剩下的就是将jar包放入项目了
参考 https://blog.csdn.net/u010277446/article/details/79203433
Maven项目
真正做项目的时候,我们总不可能每次都按照简单使用的步骤,先写.proto文件、然后编译成java或其他语言文件才能够使用。
因此便有了自动化构建,我们只需写好.proto文件,剩下的工作交由编译器执行。
创建Maven项目
创建空的Maven项目
填写项目信息
空项目结构
安装ProtoBuf插件
编译proto文件
在pom.xml中添加依赖
<build>
<pluginManagement>
<plugins>
<plugin>
<!--protobuf 插件默认的 Phase 为 GenerateCode-->
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.1</version>
<executions>
<execution>
<!--把 Compile mojo和 test compile mojo 绑定到 GenerateCode 阶段。
这样,在 GenerateCode 阶段,会执行此插件的两个 mojo。否则,在Maven 默认的 Compile 或 Test 阶段
,不会执行编译动作。-->
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.5.1</version>
</dependency>
</dependencies>
添加之前
image.png
添加之后
写.proto文件
Java
在空项目的基础上
写程序入口main
配置启动
测试环境是否搭好
试一下使用情况
public static void main(String[] args) {
AddressBookProtos.Person person = AddressBookProtos.Person
.newBuilder()
.setName("Tom")
.setId(1)
.setEmail("Email")
.addPhones(0,
AddressBookProtos.Person.PhoneNumber.newBuilder()
.setNumber("1234")
.setType(AddressBookProtos.Person.PhoneType.HOME)
.build())
.build();
AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.newBuilder()
.addPeople(0, person)//这是一个list所以用add,set的话同list使用
.addPeople(1, person)
.build();
System.out.println(addressBook.toByteString());
try {
AddressBookProtos.AddressBook addressBook1 = AddressBookProtos.AddressBook.parseFrom(addressBook.toByteArray());
System.out.println(addressBook1.toString());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
内容已经成功打印了
Web
在空项目的基础上,创建web
修改web路径
创建Artifact
配置启动
给module添加依赖包,才可以使用servlet
书写Controller
配置servlet
请求
注意:NoClassDefFoundError 与 ClassNotFoundException
NoClassDefFoundError连接时找不到
ClassNotFoundException编译时找不到
报错原因是找不到类
jar包不在了
完成之后运行!Chrome会报404但
Android
当使用kotlin编写protobuf相关当代码时
查了写资料发现protobuf不支持kotlin语言编写,发生原因与编译的顺序有关,网上也有相关的解决办法。
虽然kotlin可以与java混用,但此时此刻protobuf官方还没支持kotlin语言,所以还是乖乖用java,这里就不硬走kotlin的路线了。
插件
先来安装AndroidStudio的插件
项目插件
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3'
依赖
apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.ljf.protobuf_android"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
java {
srcDir 'src/main/java'
}
proto {//用于识别proto文件的目录
srcDir 'src/main/proto'
}
}
}
}
protobuf {//编译proto文件的任务
protoc {
artifact = 'com.google.protobuf:protoc:3.5.1'
}
generateProtoTasks {
all().each { task ->
task.builtins {
remove java
}
task.builtins {
java {}
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.protobuf:protobuf-java:3.5.1'//依赖包
}
编码
这里做了两种处理,一个是本地数据的存取,一个是获取网络数据
public class MainActivity extends Activity {
private TextView resultTv;
private Button readFileBt;
private Button sendToServerBt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
AddressBookProtos.Person person = AddressBookProtos.Person
.newBuilder()
.setName("Tom")
.setId(1)
.setEmail("Email")
.addPhones(0,
AddressBookProtos.Person.PhoneNumber.newBuilder()
.setNumber("1234")
.setType(AddressBookProtos.Person.PhoneType.HOME)
.build())
.build();
AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.newBuilder()
.addPeople(0, person)
.addPeople(1, person)
.build();//用protobuf生成对象
try {
FileOutputStream fileOutputStream = openFileOutput("proto", MODE_PRIVATE);
fileOutputStream.write(addressBook.toByteArray());//将protobuf对象的数据存到文件
fileOutputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
private void initView() {
resultTv = findViewById(R.id.resultTv);
readFileBt = findViewById(R.id.readFileBt);
sendToServerBt = findViewById(R.id.sendToServerBt);
readFileBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
FileInputStream fileInputStream = openFileInput("proto");
AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.parseFrom(fileInputStream);//由数据流生成protobuf对象
resultTv.setText("from local:\n" + addressBook.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
});
//获取网络数据
sendToServerBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL("http://10.2.24.243:8080/test");
InputStream is = url.openStream();
final AddressBookProtos.AddressBook addressBook = AddressBookProtos.AddressBook.parseFrom(is);
is.close();
runOnUiThread(new Runnable() {
@Override
public void run() {
resultTv.setText("from net:\n" + addressBook.toString());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
});
}
}
结果
由于服务开在本地所以用模拟器才可以连本地服务,但注意IP地址并不是127.0.0.1。模拟器与本地是局域网关系
网友评论