0%

Protobuf与Java互转使用

近期实现的一个功能中,由于后台需要的请求入参数据量过大,而req的载体有大小限制,所以采用了先使用pb文件存储数据上云,云文件索引作为req入参的方案。
本文记录其中使用pb与Java互转的过程。

前言

protobuf是谷歌推出的序列化协议,有比json所占字节少体积小、序列化传输快的特点。再加上其同样有平台无关的特性,protobuf得以广泛应用。

常见的使用场景:

  1. 前端与后端的通信数据载体
  2. 多端复用的数据结构,如草稿库
  3. 作为轻量级文件存储数据(本次的使用场景)

ProtoBuf转成Java过程记录

编写proto文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
syntax = "proto2";  

package demo;

option java_multiple_files = true;
option java_package = "com.darrenyuen.demo.protos";
option java_outer_classname = "DemoProtos";

message Person {
optional string name = 1;
optional int32 id = 2;
optional string email = 3;

enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}

message PhoneNumber {
optional string number = 1;
optional PhoneType type = 2 [default = HOME];
}

repeated PhoneNumber phones = 4;
}

message AddressBook {
repeated Person people = 1;
}

命令编译

proto语法是跨平台的,所以我们需要需要对应平台的编译器工具,编译成java文件,比如笔者在MacOs下,需要下载这个平台的编译工具。

1
2
3
4
protoc --java_out=输出目录 编译文件 -I=编译文件所在的文件夹

# 编译.proto文件为Kotlin文件
# protoc --proto_path=src/test/proto --kotlin_out=src/test/kotlin src/test/proto/addressbook.proto
  • protoc:Protobuf 编译器的命令行工具,用于编译 Protobuf 文件。
  • --java_out=输出目录:指定生成的 Java 代码的输出目录。编译器会将生成的代码放置在这个目录下。
  • 编译文件:Protobuf 文件的路径,这些文件包含了消息定义。
  • -I=编译文件所在的文件夹:指定 Protobuf 文件所在的目录。编译器会在这个目录下搜索指定的文件。
    执行编译后,便可拷贝生成java文件到工程中使用

集成插件编译

  1. 集成gradle插件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 根目录 build.gradle文件,引入插件
    buildscript {
    repositories {
    mavenCentral()
    }

    dependencies {
    classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18'
    }
    }
    ``
    1
    2
    // app目录 build.gradle 引入pb-java库
    implementation 'com.google.protobuf:protobuf-java:3.8.0'
1
2
3
4
// app目录 build.gradle 声明使用插件
plugins {
id 'com.google.protobuf'
}
  1. 编译配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // app目录 build.gradle
    sourceSets {
    main {
    proto {
    srcDir 'src/main/proto'
    }
    }
    }

    protobuf {
    protoc {
    artifact = 'com.google.protobuf:protoc:3.18.1'
    }
    generateProtoTasks {
    all().each { task ->
    task.builtins {
    java {
    option "lite"
    }
    // Generates Python code
    // python { }
    // Generates Kotlin code
    // kotlin { }
    }
    }
    }
    }
  2. 使用结果文件
    编译后,结果文件会生成并存储在{PROJECT_ROOT}/app/build/generated/source/proto中,可拷贝到工程中使用

Java数据序列化成pb文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
val filePath = "dir/${System.currentTimeMillis()}.pb"  
var outputStream: FileOutputStream? = null
try {
val demoParam = addressBookBuilder.build()
FileUtils.createOrExistsFile(File(filePath))
outputStream = FileOutputStream(filePath)
try {
demoParam.writeTo(outputStream)
} catch (t: Throwable) {

}
} catch (t: Throwable) {

} finally {
outputStream?.close()
}

参考文章:
如何在Android上从零使用Protobuf
适用于 Gradle 的 Protobuf 插件