Помните, когда сборка проекта занимала 30 секунд и вы даже не замечали? Потом добавили Room для кэширования, Moshi для сериализации, Dagger Hilt для DI — и вдруг ./gradlew build занимает 4 минуты. Каждую итерацию. На каждом push в feature-ветку.
Я точно помню момент, когда понял, что это ненормально: наша CI pipeline бежала 12 минут, из которых 7 — сборка. Команда из 8 разработчиков теряла в сумме больше часа каждый день только на ожидание сборки. Это боль, о которой редко говорят, потому что «сборка тормозит — это нормально для Android».
Но это не нормально. И причина в 90% случаев — kapt.
Что такое kapt и почему он медленный
kapt (Kotlin Annotation Processing Tool) — это bridge между Kotlin и Java annotation processing. Проблема в том, что annotation processing в Java-мире работает с Java AST (Abstract Syntax Tree). Kotlin-код нужно сначала скомпилировать в Java-stub'ы, потом прогнать через Java annotation processor, потом результат подмешать обратно в Kotlin-компиляцию.
Это значит, что каждый annotation processor запускает отдельный проход компиляции. Три processor'а — три дополнительных прохода. Плюс генерация stub'ов. Плюс тип-resolution. Всё это суммируется.
Вот что у нас было в build.gradle.kts:
plugins {
id("com.android.application")
kotlin("android")
id("com.google.dagger.hilt.android")
kotlin("kapt")
}
dependencies {
// Hilt
implementation("com.google.dagger:hilt-android:2.50")
kapt("com.google.dagger:hilt-android-compiler:2.50")
// Room
implementation("androidx.room:room-runtime:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
kapt("androidx.room:room-compiler:2.6.1")
// Moshi
implementation("com.squareup.moshi:moshi-kotlin:1.15.1")
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.15.1")
}
kapt {
correctErrorTypes = true
useBuildCache = true
mapDiagnosticLocations = true
}Три kapt-зависимости. Три отдельных прохода компиляции. Я замерил с помощью --profile:
./gradlew assembleDebug --profileРезультат: kapt-проходы занимали 2 минуты 48 секунд из 4 минут общей сборки. Почти 70% времени уходило на annotation processing.
KSP — что это и как работает
KSP (Kotlin Symbol Processing) — это annotation processing, написанный специально для Kotlin. Он работает напрямую с Kotlin PSI (Program Structure Interface), без промежуточных Java stub'ов. Нет двойной компиляции, нет stub generation — процессор читает Kotlin-код, анализирует символы и генерирует код.
Архитектурно KSP встроен в Kotlin compiler plugin API. Это значит, что он работает в том же процессе, что и компилятор, и может использовать типы напрямую. Нет overhead на конвертацию между Java и Kotlin типами.
Миграция: шаг за шагом
Шаг 1 — добавляем KSP-плагин
// build.gradle.kts (project level)
plugins {
id("com.google.devtools.ksp") version "2.0.0-1.0.21" apply false
}
// build.gradle.kts (app level)
plugins {
id("com.android.application")
kotlin("android")
id("com.google.dagger.hilt.android")
id("com.google.devtools.ksp") // вместо kotlin("kapt")
}Шаг 2 — заменяем kapt на ksp для Room
dependencies {
// Room — уже поддерживает KSP
implementation("androidx.room:room-runtime:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
ksp("androidx.room:room-compiler:2.6.1") // было kapt
}Room поддерживает KSP с версии 2.6.0 — это самая безболезненная миграция. Просто заменяете kapt на ksp и всё работает. Мы проверили все 47 Dao-интерфейса — ни одного breaking change.
Шаг 3 — Moshi
dependencies {
// Moshi — KSP-поддержка с версии 1.15.0
implementation("com.squareup.moshi:moshi-kotlin:1.15.1")
ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.1") // было kapt
}Moshi тоже без проблем. Единственный нюанс — если вы использовали JsonClass(generateAdapter = true) с kapt, с KSP это работает точно так же. Миграция прозрачная.
Шаг 4 — Dagger Hilt (самый сложный)
Dagger Hilt начал поддерживать KSP начиная с версии 2.50. Но есть нюанс:
dependencies {
implementation("com.google.dagger:hilt-android:2.51")
ksp("com.google.dagger:hilt-android-compiler:2.51")
}
// Если используете Hilt Testing
dependencies {
testImplementation("com.google.dagger:hilt-android-testing:2.51")
kspTest("com.google.dagger:hilt-android-compiler:2.51")
androidTestImplementation("com.google.dagger:hilt-android-testing:2.51")
kspAndroidTest("com.google.dagger:hilt-android-compiler:2.51")
}У нас были проблемы с кастомными @Module, которые генерировали типы, зависящие от сгенерированных Room-DAO. Порядок процессоров важен — KSP обрабатывает их в порядке объявления в dependencies. Если Room-процессор идёт после Hilt — может быть проблема с unresolved types. Решение: Room сначала, потом Hilt.
Удаляем kapt полностью
Когда все три процессора мигрированы на KSP, удаляем kapt-плагин:
// УДАЛЯЕМ:
// kotlin("kapt")
// kapt { ... }
// Добавляем для чистоты:
ksp {
// Room-specific: аргументы для schema export
arg("room.schemaLocation", "$projectDir/schemas")
}Результаты
После миграции всех трёх процессоров на KSP, вот замеры на том же --profile:
До: 4 минуты 02 секунды (clean build), из них kapt — 2:48.
После: 1 минута 12 секунд (clean build), из них KSP — 0:34.
Ускорение: 3.4x на clean build, 5x на incremental.
Инкрементальная сборка (изменён один файл) заняла 8 секунд вместо 45. Это самое важное для разработчика — теперь ./gradlew :app:assembleDebug при изменении одного класса занимает меньше 10 секунд, а не почти минуту.
Подводные камни
Не все библиотеки поддерживают KSP. Если у вас есть annotation processor, который не портирован на KSP — придётся оставить kapt для него. Да, можно использовать и kapt и KSP одновременно, но вы не получите полного ускорения — каждый kapt-проход всё ещё запускает полный цикл.
Ещё одна проблема: kapt.correctErrorTypes = true в KSP не нужен (и не существует). Это настройка, которая скрывает ошибки типизации в kapt-генерируемом коде. Если ваш код завязан на это поведение — при миграции могут всплыть ошибки.
Итог
Если в вашем проекте есть kapt — начните планировать миграцию на KSP. Это не сложная миграция для популярных библиотек (Room, Moshi, Dagger Hilt, Epoxy, Parcelize). Ускорение сборки в 3-5x — это не маркетинг, это реальность. А для команды из 8 человек это экономия 5+ часов каждый день. Однозначно стоит того.
Пока нет комментариев
Будьте первым, кто оставит комментарий к этой статье