mirror of
https://github.com/HighCapable/moshi-companion.git
synced 2025-10-19 00:59:27 +08:00
207 lines
9.8 KiB
Markdown
207 lines
9.8 KiB
Markdown
# Moshi Companion Documentation
|
|
|
|

|
|
|
|
Before you start using it, it is recommended that you read this document carefully so that you can better understand how it works and its functions.
|
|
|
|
You can find the demo in samples in the root directory of the project, and refer to this document for better use.
|
|
|
|
## Before You Begin
|
|
|
|
The main function of this project is to provide companion features for [Moshi](https://github.com/square/moshi). The core functionality depends on the Moshi project core. You need to use Moshi's `moshi-kotlin` and `moshi-kotlin-codegen` dependencies to generate adapter classes.
|
|
|
|
The purpose of this project is to generate "Entity Class → Adapter Class" mappings for the adapter classes generated by `moshi-kotlin-codegen` and register them to the `AdapterRegistry`, then create a custom `JsonAdapter` and set it to the `Moshi.Builder`. This avoids Moshi using `Class.forName` reflection to find adapter classes, achieving complete obfuscation of entity class names and fields, while improving performance from O(n) to O(1).
|
|
|
|
This project primarily focuses on Android projects but can still be used in pure Kotlin/Java projects.
|
|
|
|
## Quick Start
|
|
|
|
First, add dependencies to your Android/Kotlin/Java project. We recommend using Gradle's Version Catalog feature to manage dependency versions:
|
|
|
|
> `gradle/libs.versions.toml`
|
|
|
|
```toml
|
|
[versions]
|
|
agp = "8.13.0"
|
|
# Kotlin related dependency versions can be obtained from https://kotlinlang.org/docs/releases.html
|
|
kotlin = "2.2.20"
|
|
ksp = "2.2.20-2.0.3"
|
|
# Moshi version is recommended to use the version provided by 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" }
|
|
```
|
|
|
|
Replace the above `<version>` with the latest version number shown at the top.
|
|
|
|
Then, add the following configuration to your Gradle project configuration file `build.gradle` or `build.gradle.kts` where you need to use Moshi:
|
|
|
|
```kotlin
|
|
plugins {
|
|
// For Android projects
|
|
alias(libs.plugins.android.application)
|
|
alias(libs.plugins.kotlin.android)
|
|
// KSP
|
|
alias(libs.plugins.kotlin.ksp)
|
|
}
|
|
|
|
dependencies {
|
|
// Moshi related dependencies
|
|
implementation(libs.moshi.kotlin)
|
|
ksp(libs.moshi.kotlin.codegen)
|
|
|
|
// Moshi Companion related dependencies
|
|
implementation(libs.moshi.companion.api)
|
|
// Moshi Companion Codegen needs to be added after moshi-kotlin-codegen, please note the order
|
|
ksp(libs.moshi.companion.codegen)
|
|
}
|
|
```
|
|
|
|
Then we need to ensure that Moshi's ProGuard rules generation is disabled, as ProGuard rules are now managed by Moshi Companion.
|
|
|
|
```kotlin
|
|
ksp {
|
|
arg("moshi.generateProguardRules", "false")
|
|
}
|
|
```
|
|
|
|
If you don't disable Moshi's ProGuard rules generation, Moshi Companion will throw an exception at compile time prompting you to make corrections.
|
|
|
|
At this point, you have completed the dependency addition and configuration of Moshi Companion. If you are adding Moshi Companion to an existing project that uses Moshi, you only need to introduce the `moshi-companion-api` and `moshi-companion-codegen` dependencies from the steps above and add the configuration to disable ProGuard rules generation.
|
|
|
|
## Registering Adapters
|
|
|
|
Moshi Companion generates `AdapterRegistry` by reading all classes in the project that use the `@JsonClass(generateAdapter = true)` annotation. You need to manually register these adapters through `Moshi.Builder` at runtime.
|
|
|
|
After performing a Gradle build, Moshi Companion will generate an `AdapterRegistry` class in the `build/generated/ksp` directory of the project. The default generated format uses a unique package name found with the `@JsonClass(generateAdapter = true)` annotation, converts this package name to a 16-character hash as the unique identifier for the current module, and generates a package name in the following format:
|
|
|
|
```
|
|
com.highcapable.moshi.companion.r + 16-character hash + generated
|
|
```
|
|
|
|
Such as:
|
|
|
|
```
|
|
com.highcapable.moshi.companion.r1dd1c7f2a95790d7.generated
|
|
```
|
|
|
|
The class name of `AdapterRegistry` is fixed as `DefaultMoshiAdapterRegistry` and implements the `AdapterRegistry` interface.
|
|
|
|
When using Moshi, you can very simply use the extension function `addRegistry` to register this generated class to `Moshi.Builder`:
|
|
|
|
```kotlin
|
|
val moshi = Moshi.Builder()
|
|
.addRegistry(DefaultMoshiAdapterRegistry())
|
|
.build()
|
|
```
|
|
|
|
Then, you can happily continue using Moshi for JSON serialization and deserialization, completely unaffected by R8's obfuscation and optimization. Class names can be safely obfuscated, reducing size, minimizing reflection, and reducing exposure risks.
|
|
|
|
## Advanced Usage
|
|
|
|
If you need to customize the class name and package name of `AdapterRegistry`, you can achieve this by adding the following KSP parameters in `build.gradle` or `build.gradle.kts`:
|
|
|
|
```kotlin
|
|
ksp {
|
|
// Customize the package name of AdapterRegistry, if it's an Android project, it's recommended to directly use "android.namespace"
|
|
arg("moshi-companion.generateAdapterRegistryPackageName", "com.yourdomain.yourpackage")
|
|
// Customize the class name of AdapterRegistry
|
|
arg("moshi-companion.generateAdapterRegistryClassName", "YourCustomMoshiAdapterRegistry")
|
|
}
|
|
```
|
|
|
|
Such as:
|
|
|
|
```
|
|
com.yourdomain.yourpackage.generated.YourCustomMoshiAdapterRegistry
|
|
```
|
|
|
|
If you maintain a modular project focused on data models, we recommend fixing the generated `AdapterRegistry` package name and class name as shown in the example above to prevent automatically generated content from not meeting your project requirements.
|
|
|
|
If you need to generate an `AdapterRegistry` that is only accessible to the current project, you can achieve this by adding the following KSP parameter:
|
|
|
|
```kotlin
|
|
ksp {
|
|
arg("moshi-companion.generateAdapterRegistryRestrictedAccess", "true")
|
|
}
|
|
```
|
|
|
|
This way, the generated `AdapterRegistry` class will be set to `internal` and can only be accessed within the current module, avoiding misuse or abuse by other projects.
|
|
|
|
Moshi Companion's automatically generated ProGuard rules include obfuscation protection for Enum class key values. By default, only class names are obfuscated.
|
|
|
|
Please note that Moshi's default behavior is to not obfuscate Enum classes only when they are annotated with `@JsonClass(generateAdapter = false)`. When using Moshi Companion, all Enum classes will be protected from obfuscation.
|
|
|
|
If you don't need this feature, you can disable it by adding the following KSP parameter (not recommended to disable):
|
|
|
|
```kotlin
|
|
ksp {
|
|
arg("moshi-companion.proguardRulesKeepEnumClasses", "false")
|
|
}
|
|
```
|
|
|
|
If you don't want Moshi Companion to automatically generate any ProGuard rules, you can also disable it by adding the following KSP parameter (not recommended to disable):
|
|
|
|
```kotlin
|
|
ksp {
|
|
arg("moshi-companion.generateProguardRules", "false")
|
|
}
|
|
```
|
|
|
|
## Extension API
|
|
|
|
Moshi Companion provides a `TypeRef` class to simplify the use of `Types.newParameterizedType`. Its inspiration and practice come from the veteran [Gson](https://github.com/google/gson) project's `TypeToken`. You can create a type reference by extending `TypeRef`, then get the `Type` object through the `type` property.
|
|
|
|
```kotlin
|
|
val typeRef = typeRef<List<YourDataClass>>()
|
|
// Get the Type object, i.e., List<YourDataClass>
|
|
val type = typeRef.type
|
|
// Get the raw object, i.e., List
|
|
val rawType = typeRef.rawType
|
|
```
|
|
|
|
You can directly pass the obtained `type` object to the `Moshi.adapter` method to get the corresponding `JsonAdapter`.
|
|
|
|
```kotlin
|
|
val adapter = moshi.adapter<List<YourDataClass>>(typeRef.type)
|
|
```
|
|
|
|
Of course, you don't need to write it so complexly. You can directly use the `typeAdapter` extension function provided by Moshi Companion to simplify this process:
|
|
|
|
```kotlin
|
|
val adapter = moshi.typeAdapter<List<YourDataClass>>()
|
|
// Compare with the original approach
|
|
val type = Types.newParameterizedType(List::class.java, YourDataClass::class.java)
|
|
val adapter = moshi.adapter<List<YourDataClass>>(type)
|
|
```
|
|
|
|
`TypeRef` has been handled in the ProGuard rules generated by default by Moshi Companion and is completely unaffected by R8's obfuscation and optimization. Similarly, generic class names can be safely obfuscated, reducing size, minimizing reflection, and reducing exposure risks.
|
|
|
|
## Troubleshooting
|
|
|
|
If you haven't disabled Moshi Companion's ProGuard rules generation, but the ProGuard rules are not included in `shrink-rules`, you can check the generated `configuration.txt` file after R8 finishes to see if it contains "JsonAdapter".
|
|
|
|
Currently in Android projects, this issue may occur in the main project module (e.g., "app"). If the ProGuard rules don't take effect, please check if there are rule files under `build/generated/ksp/release/resources/META-INF/proguard/`. If they exist, please add the following configuration in the `buildTypes` section of `build.gradle.kts`:
|
|
|
|
```kotlin
|
|
release {
|
|
isMinifyEnabled = true
|
|
|
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
|
// Specify the ProGuard rules file generated by Moshi Companion
|
|
file("build/generated/ksp/release/resources/META-INF/proguard/").listFiles()?.firstOrNull()?.let {
|
|
proguardFiles(it)
|
|
}
|
|
}
|
|
``` |