mirror of
https://github.com/HighCapable/moshi-companion.git
synced 2025-10-18 16:49:25 +08:00
207 lines
8.9 KiB
Markdown
207 lines
8.9 KiB
Markdown
# Moshi Companion 使用文档
|
||
|
||

|
||
|
||
在开始使用之前,建议你仔细阅读此文档,以便你能更好地了解它的作用方式与功能。
|
||
|
||
你可以在项目的根目录找到 samples 中的 Demo,并参考此文档食用效果更佳。
|
||
|
||
## 开始之前
|
||
|
||
此项目的主要功能是为 [Moshi](https://github.com/square/moshi) 提供伴侣功能,核心功能依赖于 Moshi 项目核心完成,你需要使用 Moshi 的 `moshi-kotlin` 和 `moshi-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`
|
||
|
||
```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.gradle` 或 `build.gradle.kts` 中添加如下配置:
|
||
|
||
```kotlin
|
||
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 接管。
|
||
|
||
```kotlin
|
||
ksp {
|
||
arg("moshi.generateProguardRules", "false")
|
||
}
|
||
```
|
||
|
||
如果你没有关闭 Moshi 的混淆规则生成功能,Moshi Companion 将会在编译时抛出异常提示你进行修正。
|
||
|
||
至此,你已经完成了 Moshi Companion 的依赖添加和配置,如果你是在使用 Moshi 的现有项目中添加 Moshi Companion,你只需要引入上述步骤中的 `moshi-companion-api` 和 `moshi-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` 中:
|
||
|
||
```kotlin
|
||
val moshi = Moshi.Builder()
|
||
.addRegistry(DefaultMoshiAdapterRegistry())
|
||
.build()
|
||
```
|
||
|
||
然后,你就可以愉快地继续使用 Moshi 进行 JSON 的序列化和反序列化了,完全不受 R8 的混淆和优化影响,类名可以完全做到安全混淆、压缩体积、减少反射和暴露风险。
|
||
|
||
## 高级用法
|
||
|
||
如果你需要自定义 `AdapterRegistry` 的类名和包名,可以通过在 `build.gradle` 或 `build.gradle.kts` 中添加如下 KSP 参数来实现:
|
||
|
||
```kotlin
|
||
ksp {
|
||
// 自定义 AdapterRegistry 的包名,如果是 Android 项目,推荐直接使用 "android.namespace"
|
||
arg("moshi-companion.generateAdapterRegistryPackageName", "com.yourdomain.yourpackage.generated")
|
||
// 自定义 AdapterRegistry 的类名
|
||
arg("moshi-companion.generateAdapterRegistryClassName", "YourCustomMoshiAdapterRegistry")
|
||
}
|
||
```
|
||
|
||
形如:
|
||
|
||
```
|
||
com.yourdomain.yourpackage.generated.YourCustomMoshiAdapterRegistry
|
||
```
|
||
|
||
如果你会维护一个专注于数据模型的模块化项目,我们建议像上述示例这样固定生成的 `AdapterRegistry` 包名和类名,防止自动生成的内容不符合你的项目需求。
|
||
|
||
如果你需要生成仅用于当前项目可访问的 `AdapterRegistry`,可以通过添加如下 KSP 参数来实现:
|
||
|
||
```kotlin
|
||
ksp {
|
||
arg("moshi-companion.generateAdapterRegistryRestrictedAccess", "true")
|
||
}
|
||
```
|
||
|
||
这样,生成的 `AdapterRegistry` 类将被设置为 `internal`,只能在当前模块中访问,从而避免被其它项目误用或滥用。
|
||
|
||
Moshi Companion 自动生成的混淆规则包含了对 Enum 类的键值混淆保护功能,默认情况下仅对类名进行混淆。
|
||
|
||
请注意 Moshi 的默认行为是对 `@JsonClass(generateAdapter = false)` 注解的 Enum 类才不会进行混淆,在使用 Moshi Companion 后,所有 Enum 类均会被保护不被混淆。
|
||
|
||
如果你不需要这个功能,可以通过添加如下 KSP 参数来关闭 (不建议关闭):
|
||
|
||
```kotlin
|
||
ksp {
|
||
arg("moshi-companion.proguardRulesKeepEnumClasses", "false")
|
||
}
|
||
```
|
||
|
||
如果你不希望 Moshi Companion 自动生成任何混淆规则,也可以通过添加如下 KSP 参数来关闭 (不建议关闭):
|
||
|
||
```kotlin
|
||
ksp {
|
||
arg("moshi-companion.generateProguardRules", "false")
|
||
}
|
||
```
|
||
|
||
## 扩展 API
|
||
|
||
Moshi Companion 提供了 `TypeRef` 类来简化 `Types.newParameterizedType` 的使用,它的启发和实践来自老牌 [Gson](https://github.com/google/gson) 项目的 `TypeToken`,你可以通过继承 `TypeRef` 来创建一个类型引用,然后通过 `type` 属性获取 `Type` 对象。
|
||
|
||
```kotlin
|
||
val typeRef = typeRef<List<YourDataClass>>()
|
||
// 获取 Type 对象,即 List<YourDataClass>
|
||
val type = typeRef.type
|
||
// 获取原始对象,即 List
|
||
val rawType = typeRef.rawType
|
||
```
|
||
|
||
你可以直接将获取到的 `type` 对象传递给 `Moshi.adapter` 方法来获取对应的 `JsonAdapter`。
|
||
|
||
```kotlin
|
||
val adapter = moshi.adapter<List<YourDataClass>>(typeRef.type)
|
||
```
|
||
|
||
当然,你也可以不需要写的这么复杂,你可以直接使用 Moshi Companion 提供的 `typeAdapter` 扩展函数来简化这个过程:
|
||
|
||
```kotlin
|
||
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.kts` 的 `buildTypes` 中添加如下配置:
|
||
|
||
```kotlin
|
||
release {
|
||
isMinifyEnabled = true
|
||
|
||
proguardFiles(
|
||
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro",
|
||
// 指定 Moshi Companion 生成的混淆规则文件
|
||
file("build/generated/ksp/release/resources/META-INF/proguard/").listFiles()!!.first()
|
||
)
|
||
}
|
||
``` |