androidengineers.Book a session

Code Organization

Annotation Processing & KSP

article20 minHard

Annotation processing lets tools generate boilerplate code at compile time. Room generates DAOs, Hilt generates DI factories, Retrofit generates API implementations — all through annotations. KSP (Kotlin Symbol Processing) is the modern, Kotlin-first replacement for kapt.

kapt vs KSP

FeaturekaptKSP
Kotlin supportVia stub generation (slow)Native Kotlin support
Build speed~15–25% of compilation time~2× faster than kapt
IncrementalPartialFull
APIJava Element typesKotlin KSDeclaration types
MultiplatformLimitedSupported

Migration rule: If a library supports KSP, use KSP. Room, Hilt, Moshi, and Kotlin serialization all support KSP.

// build.gradle.kts — swap kapt for ksp
plugins {
    id("com.google.devtools.ksp") version "1.9.22-1.0.17"  // matches Kotlin version
}

dependencies {
    // ❌ Old: kapt
    kapt("androidx.room:room-compiler:2.6.1")

    // ✅ New: ksp
    ksp("androidx.room:room-compiler:2.6.1")
}

How Annotation Processing Works

Source code + annotations
         ↓
  Kotlin Compiler (KSP plugin runs here)
         ↓
  Processor reads @Entity, @Dao, etc.
         ↓
  Generates source files (RoomDao_Impl.kt, etc.)
         ↓
  Generated sources compiled along with your code

Writing a Simple KSP Processor

Here's a toy processor that generates a toString() for classes annotated with @AutoToString:

// 1. Define the annotation
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class AutoToString

// 2. Implement the processor
class AutoToStringProcessor(private val codeGenerator: CodeGenerator) : SymbolProcessor {
    override fun process(resolver: Resolver): List<KSAnnotated> {
        resolver.getSymbolsWithAnnotation(AutoToString::class.qualifiedName!!)
            .filterIsInstance<KSClassDeclaration>()
            .forEach { generateToString(it) }
        return emptyList()
    }

    private fun generateToString(clazz: KSClassDeclaration) {
        val packageName = clazz.packageName.asString()
        val className = clazz.simpleName.asString()
        val properties = clazz.getAllProperties().map { it.simpleName.asString() }

        val file = codeGenerator.createNewFile(
            dependencies = Dependencies(false, clazz.containingFile!!),
            packageName = packageName,
            fileName = "${className}ToStringExt"
        )

        file.writer().use { writer ->
            writer.write("package $packageName\n\n")
            val fields = properties.joinToString(", ") { "$it=\${obj.$it}" }
            writer.write("""
fun $className.toStringAuto(): String = "$className($fields)"
            """.trimIndent())
        }
    }
}

// 3. Register the processor via SymbolProcessorProvider
class AutoToStringProcessorProvider : SymbolProcessorProvider {
    override fun create(environment: SymbolProcessorEnvironment) =
        AutoToStringProcessor(environment.codeGenerator)
}
# resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
com.example.processor.AutoToStringProcessorProvider

Common KSP Use Cases in Android

LibraryAnnotationGenerated code
Room@Entity, @DaoSQL query implementations
Hilt@HiltViewModel, @InjectDI factory classes
Moshi@JsonClassJSON adapter
Kotlin Serialization@SerializableSerialization code
Retrofit(uses reflection; KSP not common here)

Debugging KSP Processors

Generated files are in build/generated/ksp/debug/kotlin/. Read them when Room or Hilt gives cryptic compile errors — the generated code often shows the actual problem.

# Find generated files
find build/generated/ksp -name "*.kt" | head -20

# Check KSP errors
./gradlew compileDebugKotlin 2>&1 | grep "e:"

Key Takeaways

ConceptRule
KSP vs kaptPrefer KSP when available — 2× faster
KSP plugin versionMust match your Kotlin version
Room with KSPksp("androidx.room:room-compiler:...") instead of kapt
Generated filesIn build/generated/ksp/ — read these when debugging annotation errors
Custom processorImplement SymbolProcessor + register SymbolProcessorProvider via ServiceLoader

YOUR LEARNING JOURNEY

0 of 177 available lessons completed

Progress saved in this browser. No account needed.
Annotation Processing & KSP | Android System Design | Android Engineers