Как Gradle собирает модули
Что проверяют
Понимаете ли вы, что происходит между нажатием Run и готовым APK, и почему многомодульный проект собирается быстрее монолита. Следом обычно спрашивают разницу между api и implementation или просят объяснить, почему сборка стала медленной.
Как отвечать
Опорная мысль: Gradle собирает не модули, а граф задач. Любая сборка проходит три фазы:
- инициализация: Gradle читает
settings.gradle.kts, где черезinclude(":app", ":core:network")перечислены модули, и создаёт проект для каждого; - конфигурация: выполняются скрипты
build.gradle.kts, применяются плагины (com.android.applicationдля приложения,com.android.libraryдля библиотек), строится граф задач. Configuration cache сохраняет результат этой фазы и пропускает её при повторном запуске; - выполнение: задачи идут в порядке зависимостей, независимые модули собираются параллельно при
org.gradle.parallel=true.
Модули связываются через implementation(project(":core:data")). Библиотечный модуль компилирует свой Kotlin и ресурсы, а app сливает манифесты и ресурсы, переводит байткод в DEX через D8, в релизе прогоняет R8 и упаковывает APK или AAB.
Скорость дают три механизма:
- up-to-date: задача пропускается, если её входы и выходы не изменились;
- build cache (
org.gradle.caching=true): результат берётся из кеша, в том числе собранного на другой машине; - compile avoidance: зависимые модули перекомпилируются, только если изменился публичный ABI модуля.
Здесь и важна разница: implementation прячет транзитивные зависимости от потребителей, api протаскивает их в чужой classpath и расширяет зону перекомпиляции. Общие настройки выносят в convention plugins в build-logic, версии в libs.versions.toml.
Где спотыкаются
Считают, что модули собираются по очереди в порядке include. Порядок определяет граф зависимостей, а не список в settings.
Ставят api везде, «чтобы работало». Правка в нижнем модуле тогда пересобирает половину проекта, и выигрыш от модуляризации пропадает.
Кладут тяжёлую логику в фазу конфигурации: чтение файлов, вызовы git, tasks.create вместо ленивого tasks.register. Такой код тормозит даже ./gradlew help и ломает configuration cache.