My first Kotlin Symbol Processing Tool for Android

Background of requirements

When we communicate with server, we deliver many payloads. the payload type is mostly JSON. we sent and received lots of JSON payloads via HTTP.
For JSON, we are using Moshi
For HTTP, we are using Retrofit

1. KSP module config

implementation("com.google.devtools.ksp:symbol-processing-api:1.4.30-1.0.0-alpha02")

2. SymbolProcessor

We are going to use SymbolProcessor as Google provided sample project.

fun init(
options: Map<String, String>,
kotlinVersion: KotlinVersion,
codeGenerator: CodeGenerator,
logger: KSPLogger
)
fun process(resolver: Resolver): List<KSAnnotated>fun finish()fun onError() {}
  1. in init() : Read Gradle script property. Set codeGenerator to member field
  2. in process() : Collect @JsonClass(generateAdapter = true)annotated class
  3. in finish() : Generate Grouped JsonAdapterFactory

3. Init()

4. process()

5. CustomKSVisitor.kt

KSVisitor is to help reading each KSNode information.

class KSVisitor<D, R> {
fun visitFile(file: KSFile, data: D): R
fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: D): R
fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: D): R
// and so on.. argument, property getter/setter
}

6. finish()

Before finishing KSP, we will generate GroupedJsonAdapterFactory.

val packageName = "com.example"
val outputStream: OutputStream = codeGenerator.createNewFile(
dependencies = Dependencies(
true,
*targets.map { it.containingFile!! }
.toTypedArray()
),
packageName = packageName,
fileName = "GroupJsonAdapterFactory"
)
outputStream.write("""
|package $packageName
|
|import com.squareup.moshi.JsonAdapter
|import com.squareup.moshi.Moshi
|import com.squareup.moshi.Types
|// import target classes & target classes json adapter
|class GroupJsonAdapterFactory: JsonAdapter.Factory {
| override fun create(): JsonAdapter<*> {
| // declare logics
| }
|}
""".trimMargin().toByteArray())
outputStream.close()

7. Resources/META-INF.services

In META-INF.services, we should expose our SymboleProcessor explicitly.

com.example.CustomSymbolProcessor

7. Consumer module

8. Wth??? My IDEA isn’t aware of KSP generated file

Yes currently, KSP generated files isn’t indexed on IDE. To use it, you should do extra work in Gradle script.

Tip

If you are doing multi module project, Duplicated class name to generate file is not good idea. So we use this

// consumer build.gradle.kts
ksp {
arg("projectName", project.name)
}
// SymbolProcessor.kt
private lateinit var projectName: String
fun init(options: Map<String, String>) {
projectName = options["projectName"]
}
fun finish() { outputStream.write("""
|class ${projectName}JsonAdapterFactory
""")
}

Conclusion

I have no idea KSP is really faster or easier than APT. Because I never write APT code generator before. but KSP is really simple and fast enough to use. And I am happy to use.

Reference

Google/KSP
Github : https://github.com/google/ksp
Sample for Android : google/ksp/playground-android
KSP adoption in AOSP : https://android-review.googlesource.com/q/ksp

Update::

com.sqaureup.moshi.internal.Util.generatedAdapter() is covering my work and internally it has ProguardConfiguration to prevent obfuscation of class also. Please refer this only for KSP usages. lol

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
SeongUg Steve Jung

SeongUg Steve Jung

Android Developer, Google Developers Experts