使用Protobuf

大约 2 分钟

使用Protobuf

Protobuf是一种语言无关、平台无关、可扩展的序列化结构化数据协议,常用于高效传输和存储结构化数据。

在本章节,我们将学习如何在Kuikly跨端层处理Protobuf数据。

在Kuikly中使用Protobuf,主要包括数据类生成编解码二进制数据传输几个步骤。

数据类生成

我们使用Square Wireopen in new window的Kotlin Multiplatform版本来实现Protobuf的数据类生成以及后续的编解码。

先看一个简单的例子,我们定义了以下Protobuf数据结构:

syntax = "proto3";

// 用户性别枚举定义
enum Gender {
  NOT_SPECIFIED = 0;
  MALE = 1;
  FEMALE = 2;
}

// 用户信息
message User {
  int64 id = 1;
  string name = 2;
  int32 age = 3;
  Gender gender = 4;
}

根据项目的kotlin版本,下载对应的Wire Compilerhttps://search.maven.org/remote_content?g=com.squareup.wire&a=wire-compiler&c=jar-with-dependencies&v=LATESTopen in new window

提示

  • LATEST替换为项目匹配的版本号

命令行执行:

java -jar wire-compiler-VERSION-jar-with-dependencies.jar \
    --proto_path=SRC_PATH \
    --kotlin_out=DST_PATH

生成的数据类在DST_PATH

// Code generated by Wire protocol buffer compiler, do not edit.
// Source: User in sample.proto
@file:Suppress("DEPRECATION")
...
/**
 * 用户信息
 */
public class User(
  @field:WireField(...)
  public val id: Long = 0L,
  @field:WireField(...)
  public val name: String = "",
  @field:WireField(...)
  public val age: Int = 0,
  @field:WireField(...)
  public val gender: Gender = Gender.NOT_SPECIFIED,
  unknownFields: ByteString = ByteString.EMPTY,
) : Message<User, Nothing>(ADAPTER, unknownFields) {
  ...
  public companion object {
    @JvmField
    public val ADAPTER: ProtoAdapter<User> = object : ProtoAdapter<User>(...) {...}
  }
}

提示

  • wire-compiler版本需要跟编解码的wire-runtime版本保持一致
  • 可以把数据类生成步骤配置成JavaExec类型的Gradle Task,在编译时动态生成,以便同时适配多个Kotlin版本

编码 / 解码

在项目的build.gradle.kts中,配置wire-runtime依赖:

dependencies {
    implementation("com.squareup.wire:wire-runtime:$WIRE_VERSION")
}

编写业务逻辑:

// 编码
val user = User(
    id = id,
    name = name,
    age = age,
    gender = gender
)
val bytes: ByteArray = user.encode()

// 解码
val user = User.ADAPTER.decode(bytes)

提示

更多用法请参考Wire官方API文档open in new window

二进制数据传输

配合自定义Module的ByteArray数据接口,以实现Protobuf的二进制数据传输/发送。

Module接口示例:

internal class BridgeModule : Module() {

    override fun moduleName(): String {
        return MODULE_NAME
    }

    fun sendProtobuf(data: ByteArray, callback: ((ByteArray) -> Unit)?) {
        // 异步调用Native方法(native侧在主线程执行),传输基本类型数组,回调基本类型
        asyncToNativeMethod(METHOD_SEND_PB, arrayOf(data)) {
            callback?.invoke(it as? ByteArray ?: byteArrayOf())
        }
    }
    ...
}
上次编辑于: