Оптимизируйте скорость сборки

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

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

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

При разработке приложения по возможности развертывайте его на устройстве под управлением Android 7.0 (уровень API 24) или выше. В новых версиях платформы Android реализованы улучшенные механизмы обновления вашего приложения, такие как среда выполнения Android (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"
        }
        ...
    }
}

Котлин

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

Поэкспериментируйте с размещением портала плагинов Gradle последним.

В 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. Используйте Android Studio, чтобы легко конвертировать изображения в WebP .

Отключить сжатие PNG

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

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

классный

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

Котлин

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 (JVM). В файле 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 , выбрав «Рефакторинг» > «Миграция на нетранзитивные классы 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 АГП 8.2
вывод cmake Выпуск № 287676077 АГП 8.2
$GRADLE_USER_HOME/.android/analytics.settings Выпуск №278767328 АГП 8.3

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