Protobuf 全称Google Protocol Buffers,是google开发的的一套用于 数据存储 ,网络通信时用于协议编解码的工具库。
这边记录下在Android中如何使用,不对编码格式原理深究。Github源码
1. protobuf环境配置
当前上下文环境
gradle: distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
android gradle: com.android.tools.build:gradle:4.2.2
java: 1.8
在项目根build.gradle中配置protobuf的gradle插件
buildscript {
...
dependencies {
...
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.13'
}
}
在app module下的build.gradle配置protobuf任务以及依赖
apply plugin: 'com.google.protobuf'
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.21.7'
}
generateProtoTasks {
all().each { task ->
task.builtins {
remove java
}
task.builtins {
java {}// 生产java源码
}
}
}
}
dependencies {
...
implementation 'com.google.protobuf:protobuf-java:3.21.7'
implementation 'com.google.protobuf:protoc:3.21.7'
}
2. 在app module的src/main下创建proto文件夹,创建proto接口定义文件。
proto协议文件有固定的语法,可在网络查找定义。
student.proto
syntax = "proto3";
package cn.rock;
option java_package = "cn.rock";
option java_outer_classname = "StudentProto";
message Student {
string name = 1;
int32 age = 2;
Sex sex = 3;
string email = 4;
map<string, string> address = 5;
repeated string course = 6;
enum Sex {
MALE = 0;
FEMALE = 1;
}
}
3.直接同步构建项目即可生成java实体。
类路径:./app/build/generated/source/proto/debug/java
4.使用
//创建实体
StudentProto.Student student = StudentProto.Student.newBuilder().setAge(12).setSex(StudentProto.Student.Sex.FEMALE).addCourse("math").build();
((TextView) findViewById(R.id.student)).setText(student.toString());
//实体转ByteString
ByteString byteString = student.toByteString();
((TextView) findViewById(R.id.byteString)).setText(byteString.toString());
//ByteString转实体
try {
student = StudentProto.Student.parseFrom(byteString);
((TextView) findViewById(R.id.student1)).setText(student.toString());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
以ByteString包括字节数组的容器,封装了数组和ByteString对象间的转换,可用于传递字节数组。
//ByteString处理字节数组
new Thread(() -> {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.tjxb);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] bytes = baos.toByteArray();
ByteString tmpByteString = ByteString.copyFrom(bytes);
byte[] tmpBytes = tmpByteString.toByteArray();
Bitmap tmpBitmap = BitmapFactory.decodeByteArray(tmpBytes, 0, bytes.length);
ImageView imageView = (ImageView) findViewById(R.id.imageView);
imageView.post(() -> imageView.setImageBitmap(tmpBitmap));
}).start();
网络中使用protobuf传输数据,以Socket传输上述Student对象为例:
客户端:
private void connectServer(StudentProto.Student student) {
try {
Socket socket = new Socket("0.0.0.0", 9998);
OutputStream os = socket.getOutputStream();
student.writeTo(os);
socket.shutdownOutput();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
服务端:
private void startServer() {
try {
ServerSocket serverSocket = new ServerSocket(9998);
while (true) {
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
StudentProto.Student student = StudentProto.Student.parseFrom(inputStream);
System.out.println("客户端: " + student);
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
Protobuf生成的实体类有对InputStream和OutputStream接口,可以直接输出。