Способы увеличить скорость работы сборки

Длительное время сборки замедляет процесс разработки. На этой странице приведены способы устранения проблем, связанных со скоростью сборки.

Общий процесс повышения скорости сборки приложения выглядит следующим образом:

  1. Оптимизируйте конфигурацию сборки, выполнив несколько действий, которые сразу же принесут пользу большинству проектов Android Studio.
  2. Профилируйте сборку, чтобы выявлять и устранять сложные узкие места, которые могут быть связаны с вашим проектом или рабочей станцией.

При разработке приложения по возможности используйте устройство с Android 7.0 (уровень API 24) или более поздней версии. В более новых версиях платформы Android реализованы более эффективные механизмы для отправки обновлений в приложение, например Android Runtime (ART) и встроенная поддержка нескольких файлов DEX.

Примечание. После первой чистой сборки вы можете заметить, что последующие сборки, как чистые, так и инкрементные, выполняются намного быстрее даже без использования каких-либо оптимизаций, описанных на этой странице. Это связано с тем, что у демона Gradle, как и у других процессов JVM, есть период "разогрева", в течение которого производительность повышается.

Оптимизируйте конфигурацию сборки

Следуйте приведенным ниже советам, чтобы ускорить сборку проекта Android Studio.

Поддерживайте актуальность инструментов

Инструменты Android получают оптимизацию сборки и новые функции почти с каждым обновлением. Некоторые советы на этой странице предполагают, что вы используете последнюю версию. Чтобы использовать последние оптимизации, обновляйте следующие компоненты:

Используйте KSP вместо kapt

Инструмент обработки аннотаций Kotlin (kapt) работает значительно медленнее, чем процессор символов Kotlin (KSP). Если вы пишете аннотированный исходный код на Kotlin и используете инструменты, которые обрабатывают аннотации (например, Room), поддерживающие KSP, вам следует перейти на KSP.

Не собирайте ненужные ресурсы

Не компилируйте и не упаковывайте ресурсы, которые не тестируются, например дополнительные локализации на другие языки и ресурсы для экранов с разной плотностью пикселей. Вместо этого укажите только один языковой ресурс и плотность экрана для варианта "dev", как показано в следующем примере:

Яркий

android {
    ...
    productFlavors {
        dev {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations "en", "xxhdpi"
        }
        ...
    }
}

Kotlin

android {
    ...
    productFlavors {
        create("dev") {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations("en", "xxhdpi")
        }
        ...
    }
}

Попробуйте поместить Gradle Plugin Portal в конец списка.

В Android все плагины находятся в репозиториях google() и mavenCentral(). Однако для сборки могут потребоваться сторонние плагины, которые разрешаются с помощью сервиса gradlePluginPortal().

Gradle выполняет поиск в репозиториях в том порядке, в котором они объявлены. Поэтому, если в репозиториях, указанных первыми, содержится большинство плагинов, производительность сборки повышается. Поэтому попробуйте поместить запись gradlePluginPortal() в конец блока репозитория в файле settings.gradle. В большинстве случаев это позволяет сократить количество лишних поисков плагинов и повысить скорость сборки.

Подробную информацию о том, как Gradle работает с несколькими хранилищами, можно найти в разделе Объявление нескольких хранилищ документации по Gradle.

Используйте статические значения конфигурации сборки в отладочной сборке

Всегда используйте статические значения для свойств, которые входят в файл манифеста или файлы ресурсов для типа отладочной сборки.

Использование динамических кодов версий, названий версий, ресурсов или любой другой логики сборки, которая изменяет файл манифеста, требует полной сборки приложения каждый раз, когда вы хотите внести изменения, даже если на самом деле для этого достаточно горячей замены. Если в конфигурации сборки требуются такие динамические свойства, изолируйте их в вариантах конечной сборки и сохраните статические значения для отладочных сборок, как показано в примере ниже.

  ...
  // Use a filter to apply onVariants() to a subset of the variants.
  onVariants(selector().withBuildType("release")) { variant ->
      // Because an app module can have multiple outputs when using multi-APK, versionCode
      // is only available on the variant output.
      // Gather the output when we are in single mode and there is no multi-APK.
      val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }

      // Create the version code generating task.
      val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
          it.outputFile.set(project.layout.buildDirectory.file("versionCode${variant.name}.txt"))
      }

      // Wire the version code from the task output.
      // map will create a lazy Provider that:
      // 1. Runs just before the consumer(s), ensuring that the producer (VersionCodeTask) has run
      //    and therefore the file is created.
      // 2. Contains task dependency information so that the consumer(s) run after the producer.
      mainOutput.versionCode.set(versionCodeTask.flatMap { it.outputFile.map { it.asFile.readText().toInt() } })
  }
  ...

  abstract class VersionCodeTask : DefaultTask() {

    @get:OutputFile
    abstract val outputFile: RegularFileProperty

    @TaskAction
    fun action() {
        outputFile.get().asFile.writeText("1.1.1")
    }
  }

Чтобы узнать, как задать динамический код версии в проекте, ознакомьтесь с рецептом setVersionsFromTask на GitHub.

Используйте статические версии зависимостей

При объявлении зависимостей в файлах build.gradle не используйте динамические номера версий (с плюсом в конце, например 'com.android.tools.build:gradle:2.+'). Это может привести к неожиданным обновлениям версий, проблемам с разрешением различий между версиями и замедлению сборки из-за того, что Gradle проверяет наличие обновлений. Вместо этого используйте статические номера версий.

Как создавать модули библиотеки

Найдите в приложении код, который можно преобразовать в модуль библиотеки Android. Такая модульная структура позволяет системе сборки компилировать только измененные модули и кешировать результаты для будущих сборок. Модульность также повышает эффективность параллельного выполнения проекта, если вы включите эту оптимизацию.

Как создавать задачи для собственной логики сборки

После того как вы создадите профиль сборки, и если в нем будет указано, что значительная часть времени сборки приходится на этап **Настройка проектов**, проверьте скрипты build.gradle и найдите код, который можно включить в специальную задачу Gradle. Перенеся часть логики сборки в задачу, вы обеспечите ее выполнение только при необходимости, сможете кешировать результаты для последующих сборок и получите возможность выполнять логику сборки параллельно, если включите параллельное выполнение проекта. Подробнее о задачах для специальной логики сборки можно узнать из официальной документации Gradle.

Совет. Если в вашей сборке много задач, вы можете создать собственные классы задач, чтобы упростить структуру файлов build.gradle. Добавьте классы в каталог project-root/buildSrc/src/main/groovy/. Gradle автоматически включит их в путь к классам для всех файлов build.gradle в проекте.

Как конвертировать изображения в формат WebP

WebP – это формат графических файлов, который обеспечивает сжатие с потерями (как JPEG), а также прозрачность (как PNG). WebP обеспечивает более эффективное сжатие, чем JPEG или PNG.

Уменьшение размера графических файлов без сжатия во время сборки может ускорить сборку, особенно если в приложении используется много графических ресурсов. Однако при распаковке изображений WebP может немного увеличиться нагрузка на ЦП устройства. Вы можете легко преобразовать изображения в формат WebP с помощью Android Studio.

Как отключить сжатие PNG-файлов

Если вы не преобразуете изображения PNG в формат WebP, то можете ускорить сборку, отключив автоматическое сжатие изображений при каждой сборке приложения.

Если вы используете плагин Android Gradle 3.0.0 или более поздней версии, сжатие PNG-файлов по умолчанию отключено для типа сборки debug. Чтобы отключить эту оптимизацию для других типов сборки, добавьте в файл build.gradle следующее:

Яркий

android {
    buildTypes {
        release {
            // Disables PNG crunching for the "release" build type.
            crunchPngs false
        }
    }
}

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Disables PNG crunching for the "release" build type.
            isCrunchPngs = false
        }
    }
}

Поскольку типы сборки и версии продукта не определяют это свойство, вам нужно вручную задать для него значение true при создании конечной версии приложения.

Эксперимент с параллельным сборщиком мусора JVM

Чтобы повысить производительность сборки, можно настроить оптимальный сборщик мусора JVM, используемый Gradle. В JDK 8 по умолчанию используется параллельный сборщик мусора, а в JDK 9 и более поздних версий – сборщик мусора G1.

Чтобы повысить производительность сборки, рекомендуем протестировать сборки Gradle с параллельным сборщиком мусора. В gradle.properties задайте следующие параметры:

org.gradle.jvmargs=-XX:+UseParallelGC

Если в этом поле уже есть другие варианты, добавьте новый:

org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC

Чтобы измерить скорость сборки с разными конфигурациями, ознакомьтесь с разделом Профилирование сборки.

Увеличьте размер кучи JVM

Если сборка выполняется медленно и в анализаторе сборки видно, что сборка мусора занимает более 15% времени сборки, увеличьте размер динамически распределяемой памяти, доступный виртуальной машине Java. В файле gradle.properties установите ограничение в 4, 6 или 8 ГБ, как показано в примере ниже.

org.gradle.jvmargs=-Xmx6g

Затем проверьте, увеличилась ли скорость сборки. Проще всего определить оптимальный размер кучи, немного увеличив лимит, а затем проверив, достаточно ли увеличилась скорость сборки.

Если вы также используете параллельный сборщик мусора JVM, вся строка будет выглядеть так:

org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g

Чтобы проанализировать ошибки памяти JVM, включите флаг HeapDumpOnOutOfMemoryError. В этом случае при нехватке памяти JVM создаст дамп кучи.

Использовать нетранзитивные классы R

Используйте нетранзитивные классы R, чтобы ускорить сборку приложений с несколькими модулями. Это позволяет избежать дублирования ресурсов, поскольку класс R каждого модуля содержит ссылки только на собственные ресурсы и не использует ссылки из зависимостей. Это позволяет ускорить сборку и избежать компиляции. Такое поведение задано по умолчанию в плагине Android Gradle версии 8.0.0 и более поздних.

Начиная с версии Android Studio Bumblebee, непереходные классы R включены по умолчанию для новых проектов. Если проект создан в более ранней версии Android Studio, обновите его, чтобы использовать нетранзитивные классы R. Для этого выберите Refactor > Migrate to Non-Transitive R Classes (Рефакторинг > Переход на нетранзитивные классы R).

Чтобы узнать больше о ресурсах приложений и классе R, ознакомьтесь с обзором ресурсов приложений.

Использовать непостоянные классы R

Используйте в приложениях и тестах поля класса R, не являющиеся константами, чтобы повысить инкрементность компиляции Java и более точно удалять неиспользуемые ресурсы. Поля класса R всегда непостоянны для библиотек, поскольку ресурсы нумеруются при упаковке APK для приложения или теста, который зависит от этой библиотеки. Такое поведение задано по умолчанию в плагине Android Gradle версии 8.0.0 и более поздних.

Отключите флаг Jetifier

Поскольку в большинстве проектов библиотеки AndroidX используются напрямую, вы можете удалить флаг Jetifier, чтобы повысить производительность сборки. Чтобы удалить флаг Jetifier, задайте значение android.enableJetifier=false в файле gradle.properties.

Анализатор сборки может проверить, можно ли безопасно удалить этот флаг, чтобы повысить производительность сборки проекта и отказаться от устаревших библиотек поддержки Android. Подробнее об устранении неполадок со сборкой…

Использовать кеш конфигурации

Кэш конфигурации позволяет Gradle записывать информацию о графе задач сборки и использовать ее в последующих сборках, чтобы Gradle не приходилось заново настраивать всю сборку.

Чтобы включить кеш конфигурации, выполните следующие действия:

  1. Убедитесь, что все плагины проекта совместимы.

    Используйте Анализатор сборки, чтобы проверить, совместим ли ваш проект с кешем конфигурации. Анализатор сборки запускает последовательность тестовых сборок, чтобы определить, можно ли включить функцию для проекта. Список поддерживаемых плагинов можно найти в задаче № 13490.

  2. Добавьте в файл gradle.properties следующий код:

      org.gradle.configuration-cache=true
      # Use this flag carefully, in case some of the plugins are not fully compatible.
      org.gradle.configuration-cache.problems=warn

Если кеш конфигурации включен, при первом запуске проекта в выходных данных сборки будет указано Calculating task graph as no configuration cache is available for tasks. При последующих запусках в выходных данных сборки будет указано Reusing configuration cache.

Чтобы узнать больше о кеше конфигурации, прочитайте статью Подробная информация о кешировании конфигурации и документацию Gradle о кеше конфигурации.

Проблемы с кешем конфигурации, появившиеся в Gradle 8.1 и плагине Android Gradle 8.1

Кэш конфигурации стал стабильным в Gradle 8.1. В этой версии также добавлено отслеживание API файлов. Вызовы, такие как File.exists(), File.isDirectory() и File.list(), записываются Gradle для отслеживания входных файлов конфигурации.

плагин Android Gradle (AGP) 8.1 использует эти API File для некоторых файлов, которые Gradle не должен считать входными данными кеша. Это приводит к дополнительной недействительности кеша при использовании с Gradle 8.1 и более поздних версий, что снижает производительность сборки. В AGP 8.1 в качестве входных данных для кэша используются следующие элементы:

Ввод Система отслеживания ошибок Исправлено в
$GRADLE_USER_HOME/android/FakeDependency.jar Проблема № 289232054 AGP 8.2
Выходные данные CMake Проблема № 287676077 AGP 8.2
$GRADLE_USER_HOME/.android/analytics.settings Проблема № 278767328 AGP 8.3

Если вы используете эти API или плагин, который их использует, время сборки может увеличиться, поскольку некоторые логические элементы сборки, использующие эти API, могут вызывать дополнительное аннулирование кеша. Подробнее о том, как отслеживать входные данные конфигурации сборки…