Files
moshi-companion/docs/guide-zh-CN.md
2025-10-07 08:26:25 +08:00

8.9 KiB
Raw Blame History

Moshi Companion 使用文档

Maven Central

在开始使用之前,建议你仔细阅读此文档,以便你能更好地了解它的作用方式与功能。

你可以在项目的根目录找到 samples 中的 Demo并参考此文档食用效果更佳。

开始之前

此项目的主要功能是为 Moshi 提供伴侣功能,核心功能依赖于 Moshi 项目核心完成,你需要使用 Moshi 的 moshi-kotlinmoshi-kotlin-codegen 依赖来生成适配器类。

此项目的目的是将 moshi-kotlin-codegen 生成的适配器类生成 "实体类 → 适配器类" 的映射注册到 AdapterRegistry 中并创建自定义的 JsonAdapter 设置到 Moshi.Builder,从而避免 Moshi 通过 Class.forName 反射查找适配器类,达到完全混淆实体类名称和字段的目的,同时性能将由 O(n) 提升到 O(1)。

此项目主要专注于 Android 项目,在纯 Kotlin/Java 项目中依然可以使用。

快速开始

首先,在你的 Android/Kotlin/Java 项目中添加依赖,我们推荐直接使用 Gradle 的 Version Catalog 功能来管理依赖版本:

gradle/libs.versions.toml

[versions]
agp = "8.13.0"
# Kotlin 相关依赖版本可从 https://kotlinlang.org/docs/releases.html 获取
kotlin = "2.2.20"
ksp = "2.2.20-2.0.3"
# Moshi 的版本建议使用 Moshi GitHub README 提供的版本
moshi = "1.15.2"
moshi-companion = "<version>"

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }

[libraries]
moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" }
moshi-kotlin-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
moshi-companion-api = { module = "com.highcapable.moshi.companion:companion-api", version.ref = "moshi-companion" }
moshi-companion-codegen = { module = "com.highcapable.moshi.companion:companion-codegen", version.ref = "moshi-companion" }

将上述 <version> 的版本替换为顶部显示的最新版本号。

然后,在你需要使用 Moshi 的 Gradle 项目配置文件 build.gradlebuild.gradle.kts 中添加如下配置:

plugins {
     // 如果是 Android 项目
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    // KSP
    alias(libs.plugins.kotlin.ksp)
}

dependencies {
    // Moshi 相关依赖
    implementation(libs.moshi.kotlin)
    ksp(libs.moshi.kotlin.codegen)

    // Moshi Companion 相关依赖
    implementation(libs.moshi.companion.api)
    // Moshi Companion Codegen 需要在 moshi-kotlin-codegen 之后添加,请注意顺序
    ksp(libs.moshi.companion.codegen)
}

然后我们需要确保关闭 Moshi 的混淆规则生成功能,因为混淆规则已经被 Moshi Companion 接管。

ksp {
    arg("moshi.generateProguardRules", "false")
}

如果你没有关闭 Moshi 的混淆规则生成功能Moshi Companion 将会在编译时抛出异常提示你进行修正。

至此,你已经完成了 Moshi Companion 的依赖添加和配置,如果你是在使用 Moshi 的现有项目中添加 Moshi Companion你只需要引入上述步骤中的 moshi-companion-apimoshi-companion-codegen 依赖,并添加关闭混淆规则生成的配置即可。

注册适配器

Moshi Companion 通过读取项目中所有使用 @JsonClass(generateAdapter = true) 注解的类来生成 AdapterRegistry,你需要在运行时通过 Moshi.Builder 手动注册这些适配器。

在执行过一次 Gradle 构建后Moshi Companion 会在项目的 build/generated/ksp 目录下生成 AdapterRegistry 类,生成的默认格式为扫描到不重复的一个存在 @JsonClass(generateAdapter = true) 注解的包名,使用这个包名转换为 16 位的 Hash 作为当前模块的唯一标识,并生成如下格式的包名:

com.highcapable.moshi.companion.r + 16 位 Hash + generated

形如:

com.highcapable.moshi.companion.r1dd1c7f2a95790d7.generated

AdapterRegistry 的类名固定为 DefaultMoshiAdapterRegistry,实现了 AdapterRegistry 接口。

在使用 Moshi 时,你可以非常简单地使用扩展函数 addRegistry 将这个生成的类注册到 Moshi.Builder 中:

val moshi = Moshi.Builder()
    .addRegistry(DefaultMoshiAdapterRegistry())
    .build()

然后,你就可以愉快地继续使用 Moshi 进行 JSON 的序列化和反序列化了,完全不受 R8 的混淆和优化影响,类名可以完全做到安全混淆、压缩体积、减少反射和暴露风险。

高级用法

如果你需要自定义 AdapterRegistry 的类名和包名,可以通过在 build.gradlebuild.gradle.kts 中添加如下 KSP 参数来实现:

ksp {
    // 自定义 AdapterRegistry 的包名,如果是 Android 项目,推荐直接使用 "android.namespace"
    arg("moshi-companion.generateAdapterRegistryPackageName", "com.yourdomain.yourpackage")
    // 自定义 AdapterRegistry 的类名
    arg("moshi-companion.generateAdapterRegistryClassName", "YourCustomMoshiAdapterRegistry")
}

形如:

com.yourdomain.yourpackage.generated.YourCustomMoshiAdapterRegistry

如果你会维护一个专注于数据模型的模块化项目,我们建议像上述示例这样固定生成的 AdapterRegistry 包名和类名,防止自动生成的内容不符合你的项目需求。

如果你需要生成仅用于当前项目可访问的 AdapterRegistry,可以通过添加如下 KSP 参数来实现:

ksp {
    arg("moshi-companion.generateAdapterRegistryRestrictedAccess", "true")
}

这样,生成的 AdapterRegistry 类将被设置为 internal,只能在当前模块中访问,从而避免被其它项目误用或滥用。

Moshi Companion 自动生成的混淆规则包含了对 Enum 类的键值混淆保护功能,默认情况下仅对类名进行混淆。

请注意 Moshi 的默认行为是对 @JsonClass(generateAdapter = false) 注解的 Enum 类才不会进行混淆,在使用 Moshi Companion 后,所有 Enum 类均会被保护不被混淆。

如果你不需要这个功能,可以通过添加如下 KSP 参数来关闭 (不建议关闭)

ksp {
    arg("moshi-companion.proguardRulesKeepEnumClasses", "false")
}

如果你不希望 Moshi Companion 自动生成任何混淆规则,也可以通过添加如下 KSP 参数来关闭 (不建议关闭)

ksp {
    arg("moshi-companion.generateProguardRules", "false")
}

扩展 API

Moshi Companion 提供了 TypeRef 类来简化 Types.newParameterizedType 的使用,它的启发和实践来自老牌 Gson 项目的 TypeToken,你可以通过继承 TypeRef 来创建一个类型引用,然后通过 type 属性获取 Type 对象。

val typeRef = typeRef<List<YourDataClass>>()
// 获取 Type 对象,即 List<YourDataClass>
val type = typeRef.type
// 获取原始对象,即 List
val rawType = typeRef.rawType

你可以直接将获取到的 type 对象传递给 Moshi.adapter 方法来获取对应的 JsonAdapter

val adapter = moshi.adapter<List<YourDataClass>>(typeRef.type)

当然,你也可以不需要写的这么复杂,你可以直接使用 Moshi Companion 提供的 typeAdapter 扩展函数来简化这个过程:

val adapter = moshi.typeAdapter<List<YourDataClass>>()
// 对比原版写法
val type = Types.newParameterizedType(List::class.java, YourDataClass::class.java)
val adapter = moshi.adapter<List<YourDataClass>>(type)

TypeRef 已经在 Moshi Companion 默认生成的混淆规则中被处理,完全不受 R8 的混淆和优化影响,同样地,泛型类的类名可以完全做到安全混淆、压缩体积、减少反射和暴露风险。

故障排查

如果你没有禁用 Moshi Companion 的混淆规则生成,但是混淆规则没有被加入到 shrink-rules,你可以在 R8 结束后检查生成的 configuration.txt 文件,查看是否包含 "JsonAdapter"。

目前在 Android 项目中这个问题可能出现在项目主模块 (例如 "app"),如果混淆规则没生效,请查看 build/generated/ksp/release/resources/META-INF/proguard/ 下有没有规则文件,如果存在,那么请在 build.gradle.ktsbuildTypes 中添加如下配置:

release {
    isMinifyEnabled = true

    proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
    // 指定 Moshi Companion 生成的混淆规则文件
    file("build/generated/ksp/release/resources/META-INF/proguard/").listFiles()?.firstOrNull()?.let {
        proguardFiles(it)
    }
}