Настройка вариантов сборки

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

Каждый вариант сборки представляет собой отдельную версию вашего приложения, которую вы можете собрать. Например, вы можете создать одну бесплатную версию своего приложения с ограниченным набором контента, а другую платную версию, включающую больше контента. Вы также можете создавать разные версии своего приложения, предназначенные для разных устройств, в зависимости от уровня API или других вариантов устройств.

Варианты сборки — это результат использования Gradle определенного набора правил для объединения настроек, кода и ресурсов, настроенных в ваших типах сборки и вариантах продукта. Хотя вы не настраиваете варианты сборки напрямую, вы настраиваете типы сборок и варианты продуктов, которые их формируют.

Например, в «демонстрационной» версии продукта могут быть указаны определенные функции и требования к устройству, такие как собственный исходный код, ресурсы и минимальные уровни API, тогда как в типе сборки «отладка» применяются другие параметры сборки и упаковки, такие как параметры отладки и подписывание. ключи. Вариант сборки, который объединяет эти два варианта, представляет собой версию вашего приложения «demoDebug» и включает в себя комбинацию конфигураций и ресурсов, включенных в «демо» вариант продукта, тип сборки «отладка» и main/ исходный набор.

Настройка типов сборки

Вы можете создавать и настраивать типы сборки внутри блока android файла build.gradle.kts на уровне модуля. Когда вы создаете новый модуль, Android Studio автоматически создает типы сборки отладки и выпуска. Хотя тип отладочной сборки не отображается в файле конфигурации сборки, Android Studio настраивает его с помощью debuggable true . Это позволяет отлаживать приложение на защищенных устройствах Android и настраивать подпись приложения с помощью общего хранилища ключей отладки.

Вы можете добавить тип сборки отладки в свою конфигурацию, если хотите добавить или изменить определенные настройки. В следующем примере указывается applicationIdSuffix для типа отладочной сборки и настраивается «промежуточный» тип сборки, который инициализируется с использованием параметров из типа отладочной сборки:

Котлин

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
        ...
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }

        getByName("debug") {
            applicationIdSuffix = ".debug"
            isDebuggable = true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        create("staging") {
            initWith(getByName("debug"))
            manifestPlaceholders["hostName"] = "internal.example.com"
            applicationIdSuffix = ".debugStaging"
        }
    }
}

классный

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
        ...
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        staging {
            initWith debug
            manifestPlaceholders = [hostName:"internal.example.com"]
            applicationIdSuffix ".debugStaging"
        }
    }
}

Примечание. Когда вы вносите изменения в файл конфигурации сборки, Android Studio требует, чтобы вы синхронизировали проект с новой конфигурацией. Чтобы синхронизировать проект, нажмите «Синхронизировать сейчас» на панели уведомлений, которая появляется при внесении изменений, или нажмите «Синхронизировать проект». с панели инструментов. Если Android Studio обнаружит какие-либо ошибки в вашей конфигурации, появится окно «Сообщения» с описанием проблемы.

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

Настройка вкусов продукта

Создание вариантов продукта аналогично созданию типов сборки. Добавьте варианты продукта в блок productFlavors в конфигурации сборки и включите нужные настройки. Варианты продукта поддерживают те же свойства, что и defaultConfig , поскольку defaultConfig фактически принадлежит классу ProductFlavor . Это означает, что вы можете предоставить базовую конфигурацию для всех вариантов в блоке defaultConfig , и каждый вариант может изменить любое из этих значений по умолчанию, например applicationId . Дополнительные сведения об идентификаторе приложения см. в разделе Установка идентификатора приложения .

Примечание. Вам все равно необходимо указать имя пакета, используя атрибут package в файле main/ manifest. Вы также должны использовать это имя пакета в исходном коде для ссылки на класс R или для разрешения любых относительных действий или регистрации службы. Это позволяет вам использовать applicationId чтобы присвоить каждому варианту продукта уникальный идентификатор для упаковки и распространения без необходимости изменять исходный код.

Все ароматизаторы должны принадлежать к именованному измерению вкусов, которое представляет собой группу вкусов продукта. Вы должны назначить все ароматы измерению вкуса; в противном случае вы получите следующую ошибку сборки.

  Error: All flavors must now belong to a named flavor dimension.
  The flavor 'flavor_name' is not assigned to a flavor dimension.

Если данный модуль указывает только одно измерение, плагин Android Gradle автоматически назначает все варианты модуля этому измерению.

В следующем примере кода создается измерение варианта с именем «версия» и добавляются «демонстрационный» и «полный» варианты продукта. Эти варианты предоставляют свои собственные applicationIdSuffix и versionNameSuffix :

Котлин

android {
    ...
    defaultConfig {...}
    buildTypes {
        getByName("debug"){...}
        getByName("release"){...}
    }
    // Specifies one flavor dimension.
    flavorDimensions += "version"
    productFlavors {
        create("demo") {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension = "version"
            applicationIdSuffix = ".demo"
            versionNameSuffix = "-demo"
        }
        create("full") {
            dimension = "version"
            applicationIdSuffix = ".full"
            versionNameSuffix = "-full"
        }
    }
}

классный

android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}

Примечание. Если у вас есть устаревшее приложение (созданное до августа 2021 г.), которое вы распространяете с помощью APK-файлов в Google Play, чтобы распространять свое приложение с использованием поддержки нескольких APK-файлов в Google Play, присвойте одинаковое значение applicationId всем вариантам и присвойте каждому варианту свой versionCode . Чтобы распространять разные варианты вашего приложения как отдельные приложения в Google Play, вам необходимо присвоить каждому варианту отдельный applicationId .

После создания и настройки вариантов продукта нажмите «Синхронизировать сейчас» на панели уведомлений. После завершения синхронизации Gradle автоматически создает варианты сборки на основе ваших типов сборки и разновидностей продукта и называет их в соответствии с <product-flavor><Build-Type> . Например, если вы создали «демо-версию» и «полную» версию продукта и сохранили типы сборки «отладка» и «релиз» по умолчанию, Gradle создаст следующие варианты сборки:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

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

Изменение идентификатора приложения для вариантов сборки

Когда вы создаете APK или AAB для своего приложения, инструменты сборки помечают приложение идентификатором приложения, определенным в блоке defaultConfig из файла build.gradle.kts , как показано в следующем примере. Однако, если вы хотите создать разные версии вашего приложения, которые будут отображаться в виде отдельных списков в Google Play Store, например «бесплатную» и «профессиональную» версию, вам необходимо создать отдельные варианты сборки , каждый из которых будет иметь свой идентификатор приложения.

В этом случае определите каждый вариант сборки как отдельный вариант продукта . Для каждого варианта внутри блока productFlavors вы можете переопределить свойство applicationId или вместо этого добавить сегмент к идентификатору приложения по умолчанию, используя applicationIdSuffix , как показано здесь:

Котлин

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
        }
        create("pro") {
            applicationIdSuffix = ".pro"
        }
    }
}

классный

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

Таким образом, идентификатором приложения для «бесплатного» варианта продукта будет «com.example.myapp.free».

Вы также можете использовать applicationIdSuffix для добавления сегмента на основе вашего типа сборки , как показано здесь:

Котлин

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}

классный

android {
    ...
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
    }
}

Поскольку Gradle применяет конфигурацию типа сборки после версии продукта, идентификатор приложения для варианта сборки «бесплатная отладка» — «com.example.myapp.free.debug». Это полезно, если вы хотите, чтобы отладочная и выпускная сборки находились на одном и том же устройстве, поскольку никакие два приложения не могут иметь одинаковый идентификатор приложения.

Если у вас есть устаревшее приложение (созданное до августа 2021 года), которое вы распространяете с помощью APK-файлов в Google Play, и вы хотите использовать один и тот же список приложений для распространения нескольких APK-файлов, каждый из которых предназначен для разных конфигураций устройств, например уровня API, тогда вам должен использовать один и тот же идентификатор приложения для каждого варианта сборки, но присваивать каждому APK другой versionCode . Для получения дополнительной информации прочитайте о поддержке нескольких APK . Это не повлияет на публикацию с использованием AAB, поскольку она использует один артефакт, который по умолчанию использует один код версии и идентификатор приложения.

Совет: Если вам нужно указать идентификатор приложения в файле манифеста, вы можете использовать заполнитель ${applicationId} в любом атрибуте манифеста. Во время сборки Gradle заменяет этот тег фактическим идентификатором приложения. Дополнительные сведения см. в разделе Вставка переменных сборки в манифест .

Объедините несколько вкусов продукта с размерами вкуса

В некоторых случаях вам может потребоваться объединить конфигурации из нескольких вариантов продукта. Например, вы можете создать разные конфигурации для «полной» и «демонстрационной» версии продукта, основанные на уровне API. Для этого плагин Android Gradle позволяет создавать несколько групп вкусов продукта в качестве измерений вкусов.

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

В следующем примере кода свойство flavorDimensions используется для создания измерения аромата «mode» для группировки «полных» и «демо» вариантов продукта, а также измерения аромата «api» для группировки конфигураций вариантов продукта на основе уровня API:

Котлин

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdk = 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.
      versionCode = 30000 + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdk = 23
      versionCode = 20000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdk = 21
      versionCode = 10000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

классный

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.

      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion 23
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion 21
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

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

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

  • Вариант сборки: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • Соответствующий APK: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • Например,
    Вариант сборки: minApi24DemoDebug
    Соответствующий APK: app-minApi24-demo-debug.apk

В дополнение к каталогам исходного набора, которые можно создать для каждого отдельного варианта продукта и варианта сборки, вы также можете создавать каталоги исходного набора для каждой комбинации вариантов продукта. Например, вы можете создавать и добавлять исходные коды Java в каталог src/demoMinApi24/java/ , и Gradle использует эти источники только при создании варианта, сочетающего эти два варианта продукта.

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

Фильтровать варианты

Gradle создает вариант сборки для каждой возможной комбинации вариантов продукта и типов сборки, которые вы настраиваете. Однако могут существовать определенные варианты сборки, которые вам не нужны или которые не имеют смысла в контексте вашего проекта. Чтобы удалить определенные конфигурации вариантов сборки, создайте фильтр вариантов в файле build.gradle.kts на уровне модуля.

Используя в качестве примера конфигурацию сборки из предыдущего раздела, предположим, что вы планируете поддерживать только уровни API 23 и выше для демо-версии приложения. Вы можете использовать variantFilter , чтобы отфильтровать все конфигурации вариантов сборки, которые сочетают в себе варианты продукта «minApi21» и «demo»:

Котлин

android {
  ...
  buildTypes {...}

  flavorDimensions += listOf("api", "mode")
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enable = false
        }
    }
}
...

классный

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

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

Создание исходных наборов

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

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

Gradle ожидает, что файлы и каталоги исходного набора будут организованы определенным образом, аналогично набору main/ source. Например, Gradle ожидает, что файлы классов Kotlin или Java, специфичные для вашего «отладочного» типа сборки, будут расположены в каталогах src/debug/kotlin/ или src/debug/java/ .

Плагин Android Gradle предоставляет полезную задачу Gradle, которая показывает, как организовать файлы для каждого типа сборки, разновидности продукта и варианта сборки. Например, следующий пример выходных данных задачи описывает, где Gradle ожидает найти определенные файлы для типа сборки «отладка»:

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: debugCompile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Kotlin sources: [app/src/debug/kotlin, app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

Чтобы просмотреть этот вывод, выполните следующие действия:

  1. Нажмите Gradle на панели окна инструментов.
  2. Перейдите в MyApplication > Tasks > android и дважды щелкните sourceSets .

    Чтобы увидеть папку «Задачи» , вы должны позволить Gradle создать список задач во время синхронизации. Для этого выполните следующие действия:

    1. Нажмите «Файл» > «Настройки» > «Экспериментальный» ( Android Studio > «Настройки» > «Экспериментальный» в macOS).
    2. Снимите флажок « Не создавать список задач Gradle во время синхронизации Gradle» .
  3. После того, как Gradle выполнит задачу, откроется окно «Выполнить» для отображения результатов.

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

Когда вы создаете новый вариант сборки, Android Studio не создает для вас каталоги исходного набора, но предоставляет вам несколько вариантов, которые могут вам помочь. Например, чтобы создать только каталог java/ для вашего типа сборки «отладка»:

  1. Откройте панель «Проект» и выберите представление «Проект» в меню вверху панели.
  2. Перейдите в MyProject/app/src/ .
  3. Щелкните правой кнопкой мыши каталог src и выберите «Создать» > «Каталог» .
  4. В меню «Наборы исходного кода Gradle» выберите «full/java» .
  5. Нажмите Enter .

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

Например, чтобы создать XML-файл значений для вашего типа сборки «отладка»:

  1. На панели «Проект» щелкните правой кнопкой мыши каталог src и выберите «Создать» > «XML» > «XML-файл значений» .
  2. Введите имя XML-файла или оставьте имя по умолчанию.
  3. В меню рядом с Target Source Set выберите debug .
  4. Нажмите «Готово» .

Поскольку в качестве целевого исходного набора был указан тип сборки «отладка», Android Studio автоматически создает необходимые каталоги при создании XML-файла. Результирующая структура каталогов выглядит, как показано на рисунке 1.

Рис. 1. Новые каталоги исходного набора для типа сборки «отладка».

Активные наборы источников имеют на значках зеленый индикатор, показывающий, что они активны. Набор исходного кода debug имеет суффикс [main] , чтобы показать, что он будет объединен с main набором исходного кода.

Используя ту же процедуру, вы также можете создать каталоги исходного набора для разновидностей продукта, таких как src/demo/ , и вариантов сборки, таких как src/demoDebug/ . Кроме того, вы можете создавать наборы исходных кодов для тестирования, ориентированные на определенные варианты сборки, например src/androidTestDemoDebug/ . Чтобы узнать больше, прочитайте о тестировании наборов исходных кодов .

Изменение конфигураций исходного набора по умолчанию

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

Блок sourceSets должен находиться в блоке android . Вам не нужно перемещать исходные файлы; вам нужно только предоставить Gradle путь(и) относительно файла build.gradle.kts на уровне модуля, где Gradle может найти файлы для каждого компонента исходного набора. Чтобы узнать, какие компоненты вы можете настроить и можно ли сопоставить их с несколькими файлами или каталогами, см. Справочник по API плагина Android Gradle .

Следующий пример кода сопоставляет источники из каталога app/other/ с определенными компонентами main набора исходного кода и изменяет корневой каталог набора исходного кода androidTest :

Котлин

android {
  ...
  // Encapsulates configurations for the main source set.
  sourceSets.getByName("main") {
    // Changes the directory for Java sources. The default directory is
    // 'src/main/java'.
    java.setSrcDirs(listOf("other/java"))

    // If you list multiple directories, Gradle uses all of them to collect
    // sources. Because Gradle gives these directories equal priority, if
    // you define the same resource in more than one directory, you receive an
    // error when merging resources. The default directory is 'src/main/res'.
    res.setSrcDirs(listOf("other/res1", "other/res2"))

    // Note: Avoid specifying a directory that is a parent to one
    // or more other directories you specify. For example, avoid the following:
    // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
    // Specify either only the root 'other/res1' directory or only the
    // nested 'other/res1/layouts' and 'other/res1/strings' directories.

    // For each source set, you can specify only one Android manifest.
    // By default, Android Studio creates a manifest for your main source
    // set in the src/main/ directory.
    manifest.srcFile("other/AndroidManifest.xml")
    ...
  }

  // Create additional blocks to configure other source sets.
  sourceSets.getByName("androidTest") {
      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
  }
}
...

классный

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you receive an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: Avoid specifying a directory that is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // Specify either only the root 'other/res1' directory or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

Обратите внимание, что исходный каталог может принадлежать только одному исходному набору. Например, вы не можете использовать одни и те же источники тестов для наборов исходных текстов test и androidTest . Это связано с тем, что Android Studio создает отдельные модули IntelliJ для каждого исходного набора и не может поддерживать дублирование корней контента в исходных наборах.

Сборка с использованием исходных наборов

Вы можете использовать каталоги исходного набора для хранения кода и ресурсов, которые вы хотите упаковать только с определенными конфигурациями. Например, если вы создаете вариант сборки «demoDebug», который является перекрестным продуктом варианта продукта «демо» и типа сборки «отладка», Gradle просматривает эти каталоги и присваивает им следующий приоритет:

  1. src/demoDebug/ (набор исходного кода варианта сборки)
  2. src/debug/ (исходный набор типа сборки)
  3. src/demo/ (набор исходных кодов продукта)
  4. src/main/ (основной исходный набор)

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

Если вы объединяете несколько вкусов продукта , приоритет между вкусами продуктов определяется измерением вкуса, к которому они принадлежат. При перечислении измерений вкуса с помощью свойства android.flavorDimensions варианты продукта, принадлежащие первому указанному вами измерению вкуса, имеют более высокий приоритет, чем те, которые принадлежат второму измерению вкуса, и так далее. Кроме того, наборы источников, которые вы создаете для комбинаций вариантов продукта, имеют более высокий приоритет, чем наборы источников, принадлежащие отдельным вариантам продукта.

Порядок приоритетов определяет, какой набор исходных кодов имеет более высокий приоритет, когда Gradle объединяет код и ресурсы. Поскольку каталог исходного набора demoDebug/ скорее всего, содержит файлы, специфичные для этого варианта сборки, если demoDebug/ включает файл, который также определен в debug/ , Gradle использует файл в исходном наборе demoDebug/ . Аналогично, Gradle присваивает файлам в типе сборки и исходном коде продукта более высокий приоритет, чем те же файлы в main/ . Gradle учитывает этот порядок приоритетов при применении следующих правил сборки:

  • Весь исходный код в каталогах kotlin/ или java/ компилируется вместе для создания единого вывода.

    Примечание. Для данного варианта сборки Gradle выдает ошибку сборки, если обнаруживает два или более каталогов исходного набора, в которых определен один и тот же класс Kotlin или Java. Например, при создании приложения отладки вы не можете определить одновременно src/debug/Utility.kt и src/main/Utility.kt , поскольку Gradle просматривает оба этих каталога во время процесса сборки и выдает ошибку «дубликат класса». . Если вам нужны разные версии Utility.kt для разных типов сборок, каждый тип сборки должен определять свою собственную версию файла и не включать ее в main/ исходный набор.

  • Манифесты объединяются в один манифест. Приоритет задается в том же порядке, что и список в предыдущем примере. То есть параметры манифеста для типа сборки переопределяют параметры манифеста для разновидности продукта и т. д. Чтобы узнать больше, прочитайте о слиянии манифестов .
  • Файлы в каталогах values/ объединяются вместе. Если два файла имеют одинаковое имя, например два файла strings.xml , приоритет присваивается в том же порядке, что и список в предыдущем примере. То есть значения, определенные в файле в исходном наборе типа сборки, переопределяют значения, определенные в том же файле в версии продукта, и так далее.
  • Ресурсы в каталогах res/ и asset/ упакованы вместе. Если в двух или более исходных наборах определены ресурсы с одинаковым именем, приоритет присваивается в том же порядке, что и список в предыдущем примере.
  • Gradle придает ресурсам и манифестам, включенным в зависимости модулей библиотеки, самый низкий приоритет при создании приложения.

Объявить зависимости

Чтобы настроить зависимость для конкретного варианта сборки или набора исходного кода для тестирования , добавьте префикс имени варианта сборки или набора исходного кода для тестирования перед ключевым словом Implementation , как показано в следующем примере:

Котлин

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    "freeImplementation"(project(":mylibrary"))

    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

классный

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    freeImplementation project(":mylibrary")

    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

Дополнительные сведения о настройке зависимостей см. в разделе Добавление зависимостей сборки .

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

Плагин Android Gradle 3.0.0 и выше включает новый механизм зависимостей, который автоматически сопоставляет варианты при использовании библиотеки. Это означает, что вариант debug приложения автоматически использует вариант debug библиотеки и так далее. Это также работает при использовании разновидностей: вариант freeDebug приложения будет использовать вариант freeDebug библиотеки.

Чтобы плагин точно сопоставлял варианты, вам необходимо предоставить соответствующие резервные варианты , как описано в следующем разделе, для случаев, когда прямое соответствие невозможно.

Например, предположим, что ваше приложение настраивает тип сборки, называемый «промежуточный», но одна из его библиотечных зависимостей этого не делает. Когда плагин попытается создать «промежуточную» версию вашего приложения, он не будет знать, какую версию библиотеки использовать, и вы увидите сообщение об ошибке, подобное следующему:

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

Устранение ошибок сборки, связанных с сопоставлением вариантов.

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

Ниже приведен список проблем, связанных с сопоставлением зависимостей с учетом вариантов, и способы их решения с помощью свойств DSL:

  • Ваше приложение включает тип сборки, которого нет в зависимости от библиотеки.

    Например, ваше приложение включает в себя «промежуточный» тип сборки, но зависимость включает только типы сборки «отладка» и «выпуск».

    Обратите внимание: проблем не возникает, если зависимость библиотеки включает тип сборки, которого нет в вашем приложении. Это потому, что плагин никогда не запрашивает этот тип сборки из зависимости.

    Используйте matchingFallbacks , чтобы указать альтернативные совпадения для данного типа сборки, как показано здесь:

    Котлин

    // In the app's build.gradle.kts file.
    android {
        buildTypes {
            getByName("debug") {}
            getByName("release") {}
            create("staging") {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks += listOf("debug", "qa", "release")
            }
        }
    }

    классный

    // In the app's build.gradle file.
    android {
        buildTypes {
            debug {}
            release {}
            staging {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks = ['debug', 'qa', 'release']
            }
        }
    }
    
  • Для данного измерения вкуса, который существует как в приложении, так и в зависимости от его библиотеки, ваше приложение включает варианты, которых нет в библиотеке.

    Например, и ваше приложение, и его библиотеки включают в себя «уровневое» измерение вкуса. Однако измерение «уровень» в приложении включает «бесплатные» и «платные» варианты, а зависимость включает только «демонстрационные» и «платные» варианты для одного и того же измерения.

    Обратите внимание, что для данного измерения разновидности, которое существует как в приложении, так и в зависимостях его библиотеки, не возникает проблем, если библиотека включает в себя вариант продукта, которого нет в вашем приложении. Это потому, что плагин никогда не запрашивает этот вариант из зависимости.

    Используйте matchingFallbacks , чтобы указать альтернативные совпадения для «бесплатного» продукта приложения, как показано здесь:

    Котлин

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions += "tier"
        productFlavors {
            create("paid") {
                dimension = "tier"
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            create("free") {
                dimension = "tier"
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks += listOf("demo", "trial")
            }
        }
    }
    

    классный

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions 'tier'
        productFlavors {
            paid {
                dimension 'tier'
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            free {
                dimension 'tier'
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks = ['demo', 'trial']
            }
        }
    }
    
  • Зависимость библиотеки включает в себя измерение вкуса, которого нет в вашем приложении.

    Например, зависимость библиотеки включает варианты для измерения «minApi», но ваше приложение включает варианты только для измерения «уровень». Когда вы хотите создать версию своего приложения «freeDebug», плагин не знает, использовать ли версию зависимости «minApi23Debug» или «minApi18Debug».

    Обратите внимание: если ваше приложение включает в себя измерение вкуса, которого нет в зависимости от библиотеки, проблем не возникает. Это связано с тем, что плагин соответствует вариантам только тех измерений, которые существуют в зависимости. Например, если зависимость не включает измерение для ABI, версия вашего приложения «freeX86Debug» будет использовать версию зависимости «freeDebug».

    Используйте missingDimensionStrategy в блоке defaultConfig , чтобы указать вариант по умолчанию, который плагин будет выбирать из каждого отсутствующего измерения, как показано в следующем примере. Вы также можете переопределить свой выбор в блоке productFlavors , чтобы для каждого варианта можно было указать другую стратегию сопоставления для отсутствующего измерения.

    Котлин

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy("minApi", "minApi18", "minApi23")
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy("abi", "x86", "arm64")
        }
        flavorDimensions += "tier"
        productFlavors {
            create("free") {
                dimension = "tier"
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the "minApi" dimension.
                missingDimensionStrategy("minApi", "minApi23", "minApi18")
            }
            create("paid") {}
        }
    }
    

    классный

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy 'abi', 'x86', 'arm64'
        }
        flavorDimensions 'tier'
        productFlavors {
            free {
                dimension 'tier'
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the 'minApi' dimension.
                missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
            }
            paid {}
        }
    }
    

Дополнительные сведения см. в matchingFallbacks и missingDimensionStrategy в справочнике по DSL подключаемого модуля Android Gradle.

Настройка параметров подписи

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

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

  1. Создайте хранилище ключей. Хранилище ключей — это двоичный файл, содержащий набор закрытых ключей. Вы должны хранить хранилище ключей в безопасном и надежном месте.
  2. Создайте закрытый ключ. Закрытый ключ используется для подписи вашего приложения для распространения и никогда не включается в приложение и не передается неавторизованным третьим лицам.
  3. Добавьте конфигурацию подписи в файл build.gradle.kts на уровне модуля:

    Котлин

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            create("release") {
                storeFile = file("myreleasekey.keystore")
                storePassword = "password"
                keyAlias = "MyReleaseKey"
                keyPassword = "password"
            }
        }
        buildTypes {
            getByName("release") {
                ...
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }

    классный

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            release {
                storeFile file("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }

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

Чтобы получить эти пароли из переменных среды:

Котлин

storePassword = System.getenv("KSTOREPWD")
keyPassword = System.getenv("KEYPWD")

классный

storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")

Альтернативно вы можете загрузить хранилище ключей из локального файла свойств. По соображениям безопасности не добавляйте этот файл в систему контроля версий. Вместо этого настройте его локально для каждого разработчика. Дополнительные сведения см. в статье Удаление информации о подписи из файлов сборки .

После завершения этого процесса вы сможете распространять свое приложение и публиковать его в Google Play.

Предупреждение. Храните хранилище ключей и закрытый ключ в безопасном и надежном месте и убедитесь, что у вас есть их надежные резервные копии. Если вы используете подписывание приложений Play и потеряли ключ загрузки, вы можете запросить сброс с помощью Play Console. Если вы публикуете приложение без подписи приложения Play (для приложений, созданных до августа 2021 г.) и теряете ключ подписи приложения, вы не сможете публиковать какие-либо обновления своего приложения, поскольку вы всегда должны подписывать все версии своего приложения с помощью тот же ключ.

Подписание приложений Wear OS

При публикации приложений Wear OS APK-файл часов и дополнительный APK-файл телефона должны быть подписаны одним и тем же ключом. Дополнительные сведения об упаковке и подписании приложений Wear OS см. в разделе Упаковка и распространение приложений Wear .

,

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

Каждый вариант сборки представляет собой отдельную версию вашего приложения, которую вы можете собрать. Например, вы можете создать одну бесплатную версию своего приложения с ограниченным набором контента, а другую платную версию, включающую больше контента. Вы также можете создавать разные версии своего приложения, предназначенные для разных устройств, в зависимости от уровня API или других вариантов устройств.

Варианты сборки — это результат использования Gradle определенного набора правил для объединения настроек, кода и ресурсов, настроенных в ваших типах сборки и вариантах продукта. Хотя вы не настраиваете варианты сборки напрямую, вы настраиваете типы сборок и варианты продуктов, которые их формируют.

Например, в «демонстрационной» версии продукта могут быть указаны определенные функции и требования к устройству, такие как собственный исходный код, ресурсы и минимальные уровни API, тогда как в типе сборки «отладка» применяются другие параметры сборки и упаковки, такие как параметры отладки и подписывание. ключи. Вариант сборки, который объединяет эти два варианта, представляет собой версию вашего приложения «demoDebug» и включает в себя комбинацию конфигураций и ресурсов, включенных в «демо» вариант продукта, тип сборки «отладка» и main/ исходный набор.

Настройка типов сборки

Вы можете создавать и настраивать типы сборки внутри блока android файла build.gradle.kts на уровне модуля. Когда вы создаете новый модуль, Android Studio автоматически создает типы сборки отладки и выпуска. Хотя тип отладочной сборки не отображается в файле конфигурации сборки, Android Studio настраивает его с помощью debuggable true . Это позволяет отлаживать приложение на защищенных устройствах Android и настраивать подпись приложения с помощью общего хранилища ключей отладки.

Вы можете добавить тип сборки отладки в свою конфигурацию, если хотите добавить или изменить определенные настройки. В следующем примере указывается applicationIdSuffix для типа отладочной сборки и настраивается «промежуточный» тип сборки, который инициализируется с использованием параметров из типа отладочной сборки:

Котлин

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
        ...
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }

        getByName("debug") {
            applicationIdSuffix = ".debug"
            isDebuggable = true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        create("staging") {
            initWith(getByName("debug"))
            manifestPlaceholders["hostName"] = "internal.example.com"
            applicationIdSuffix = ".debugStaging"
        }
    }
}

классный

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
        ...
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        staging {
            initWith debug
            manifestPlaceholders = [hostName:"internal.example.com"]
            applicationIdSuffix ".debugStaging"
        }
    }
}

Примечание. Когда вы вносите изменения в файл конфигурации сборки, Android Studio требует, чтобы вы синхронизировали проект с новой конфигурацией. Чтобы синхронизировать проект, нажмите «Синхронизировать сейчас» на панели уведомлений, которая появляется при внесении изменений, или нажмите «Синхронизировать проект». с панели инструментов. Если Android Studio обнаружит какие-либо ошибки в вашей конфигурации, появится окно «Сообщения» с описанием проблемы.

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

Настройка вкусов продукта

Создание вариантов продукта аналогично созданию типов сборки. Добавьте варианты продукта в блок productFlavors в конфигурации сборки и включите нужные настройки. Варианты продукта поддерживают те же свойства, что и defaultConfig , поскольку defaultConfig фактически принадлежит классу ProductFlavor . Это означает, что вы можете предоставить базовую конфигурацию для всех вариантов в блоке defaultConfig , и каждый вариант может изменить любое из этих значений по умолчанию, например applicationId . Дополнительные сведения об идентификаторе приложения см. в разделе Установка идентификатора приложения .

Примечание. Вам все равно необходимо указать имя пакета, используя атрибут package в файле main/ manifest. Вы также должны использовать это имя пакета в исходном коде для ссылки на класс R или для разрешения любых относительных действий или регистрации службы. Это позволяет вам использовать applicationId чтобы присвоить каждому варианту продукта уникальный идентификатор для упаковки и распространения без необходимости изменять исходный код.

Все ароматизаторы должны принадлежать к именованному измерению вкусов, которое представляет собой группу вкусов продукта. Вы должны назначить все ароматы измерению вкуса; в противном случае вы получите следующую ошибку сборки.

  Error: All flavors must now belong to a named flavor dimension.
  The flavor 'flavor_name' is not assigned to a flavor dimension.

Если данный модуль указывает только одно измерение, плагин Android Gradle автоматически назначает все варианты модуля этому измерению.

В следующем примере кода создается измерение варианта с именем «версия» и добавляются «демонстрационный» и «полный» варианты продукта. Эти варианты предоставляют свои собственные applicationIdSuffix и versionNameSuffix :

Котлин

android {
    ...
    defaultConfig {...}
    buildTypes {
        getByName("debug"){...}
        getByName("release"){...}
    }
    // Specifies one flavor dimension.
    flavorDimensions += "version"
    productFlavors {
        create("demo") {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension = "version"
            applicationIdSuffix = ".demo"
            versionNameSuffix = "-demo"
        }
        create("full") {
            dimension = "version"
            applicationIdSuffix = ".full"
            versionNameSuffix = "-full"
        }
    }
}

классный

android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}

Примечание. Если у вас есть устаревшее приложение (созданное до августа 2021 г.), которое вы распространяете с помощью APK-файлов в Google Play, чтобы распространять свое приложение с использованием поддержки нескольких APK-файлов в Google Play, присвойте одинаковое значение applicationId всем вариантам и присвойте каждому варианту свой versionCode . Чтобы распространять разные варианты вашего приложения как отдельные приложения в Google Play, вам необходимо присвоить каждому варианту отдельный applicationId .

После создания и настройки вариантов продукта нажмите «Синхронизировать сейчас» на панели уведомлений. После завершения синхронизации Gradle автоматически создает варианты сборки на основе ваших типов сборки и разновидностей продукта и называет их в соответствии с <product-flavor><Build-Type> . Например, если вы создали «демо-версию» и «полную» версию продукта и сохранили типы сборки «отладка» и «релиз» по умолчанию, Gradle создаст следующие варианты сборки:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

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

Измените идентификатор приложения для вариантов сборки

Когда вы создаете APK или AAB для вашего приложения, инструменты сборки помечает приложение с идентификатором приложения, определенным в блоке defaultConfig из файла build.gradle.kts , как показано в следующем примере. Однако, если вы хотите создать разные версии вашего приложения, чтобы отображаться в виде отдельных списков в Google Play Store, таких как версия «бесплатно» и «Pro», вам необходимо создать отдельные варианты сборки , каждый из которых имеет другой идентификатор приложения.

В этом случае определите каждый вариант сборки как отдельный вкус продукта . Для каждого вкуса внутри блока productFlavors вы можете переопределить свойство applicationId , или вместо этого вы можете добавить сегмент к идентификатору приложения по умолчанию, используя applicationIdSuffix , как показано здесь:

Котлин

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
        }
        create("pro") {
            applicationIdSuffix = ".pro"
        }
    }
}

Groovy

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

Таким образом, идентификатор приложения для «бесплатного» аромата продукта является «com.example.myapp.free».

Вы также можете использовать applicationIdSuffix для добавления сегмента на основе вашего типа сборки , как показано здесь:

Котлин

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}

Groovy

android {
    ...
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
    }
}

Поскольку Gradle применяет конфигурацию типа сборки после аромата продукта, идентификатор приложения для варианта сборки «Бесплатная отладка» - "com.example.myapp.free.debug". Это полезно, когда вы хотите иметь как отладку, так и настройку выпуска на одном и том же устройстве, потому что никакие два приложения не могут иметь одинакового идентификатора приложения.

Если у вас есть устаревшее приложение (созданное до августа 2021 года), которое вы распространяете с помощью APK в Google Play, и вы хотите использовать один и тот же список приложений для распределения нескольких APK, которые нацелены на различную конфигурацию устройства, такие как уровень API, тогда вы Должен использовать один и тот же идентификатор приложения для каждого варианта сборки, но дайте каждому APK различный versionCode . Для получения дополнительной информации прочитайте о нескольких поддержке APK . Публикация с использованием AABS не зависит от того, что он использует один артефакт, который по умолчанию использует один код версии и идентификатор приложения.

Совет: если вам нужно ссылаться на идентификатор приложения в вашем манифестном файле, вы можете использовать заполнитель ${applicationId} в любом атрибуте Manifest. Во время сборки Градл заменяет этот тег фактическим идентификатором приложения. Для получения дополнительной информации см. Переменные ввода в манифест .

Объедините несколько ароматов продукта с ароматными размерами

В некоторых случаях вы можете объединить конфигурации из нескольких ароматов продукта. Например, вы можете создать различные конфигурации для «полных» и «демонстрационных» вкусов продукта, которые основаны на уровне API. Для этого плагин Android Gradle позволяет создавать несколько групп ароматов продуктов в качестве ароматных размеров.

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

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

Котлин

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdk = 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.
      versionCode = 30000 + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdk = 23
      versionCode = 20000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdk = 21
      versionCode = 10000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

Groovy

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.

      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion 23
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion 21
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

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

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

  • Вариант сборки: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • Соответствующий APK: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • Например,
    Вариант сборки: minApi24DemoDebug
    Соответствующий APK: app-minApi24-demo-debug.apk

В дополнение к каталогам исходных наборов, которые вы можете создать для каждого отдельного варианта продукта и создания, вы также можете создавать каталоги наборов источников для каждой комбинации вкусов продукта. Например, вы можете создавать и добавлять источники Java в src/demoMinApi24/java/ Directory, и Gradle использует эти источники только при создании варианта, который сочетает в себе эти два вкуса продукта.

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

Фильтрующие варианты

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

Используя конфигурацию сборки из предыдущего раздела в качестве примера, предположим, что вы планируете поддерживать только уровни API 23 и выше для демонстрационной версии приложения. Вы можете использовать блок variantFilter , чтобы отфильтровать все конфигурации вариантов сборки, которые объединяют ароматы «minapi21» и «демо»:

Котлин

android {
  ...
  buildTypes {...}

  flavorDimensions += listOf("api", "mode")
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enable = false
        }
    }
}
...

Groovy

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

После того, как вы добавите вариант фильтра в свою конфигурацию сборки и нажмите Sync Now в панели уведомлений, Gradle игнорирует любые варианты сборки, которые соответствуют указанным условиям. Варианты сборки больше не появляются в меню, когда вы нажимаете на Build> Выберите вариант Build из строки меню или вариантов сборки В панели окна инструмента.

Создать исходные наборы

По умолчанию Android Studio создает main/ исходный набор и каталоги для всего, что вы хотите поделиться между всеми вариантами сборки. Тем не менее, вы можете создать новые наборы источников для управления именно тем, какие файлы Gradle компилируют и пакеты для конкретных типов сборки, вкуса продуктов, комбинации ароматов продукта (при использовании ароматных измерений ) и вариантов сборки.

Например, вы можете определить базовую функциональность в main/ исходных наборах и использовать исходные наборы вкуса продукта, чтобы изменить брендинг вашего приложения для разных клиентов или включать специальные разрешения и функции журнала только для вариантов сборки, которые используют тип сборки отладки.

Gradle ожидает, что исходные файлы и каталоги будут организованы определенным образом, аналогично main/ исходному набору. Например, Gradle ожидает, что файлы класса Kotlin или Java, специфичные для вашего типа «отладки», будут расположены в src/debug/kotlin/ или src/debug/java/ Directory.

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

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: debugCompile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Kotlin sources: [app/src/debug/kotlin, app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

Чтобы просмотреть этот вывод, выполните следующее:

  1. Нажмите Gradle в панели окна инструмента.
  2. Перейдите к MyApplication> Задачи> Android и дважды щелкните источники .

    Чтобы увидеть папку задач , вы должны позволить Gradle создать список задач во время синхронизации. Для этого следуйте этим шагам:

    1. Нажмите «Файл»> «Настройки»> «Экспериментальный» ( Android Studio> Настройки> Экспериментальные на MacOS).
    2. Deselect Не создайте список задач Gradle во время синхронизации Gradle .
  3. После того, как Gradle выполняет задачу, окно запуска открывается для отображения вывода.

ПРИМЕЧАНИЕ. Вывод задачи также показывает, как организовать исходные наборы для файлов, которые вы хотите использовать для запуска тестов для вашего приложения, таких как наборы исходных исследований test/ и androidTest/ Testing .

Когда вы создаете новый вариант сборки, Android Studio не создает для вас каталоги исходных наборов, но дает вам несколько вариантов, которые помогут вам. Например, чтобы создать только java/ Directory для вашего типа «отладки»:

  1. Откройте панель проекта и выберите представление проекта в меню в верхней части панели.
  2. Перейдите в MyProject/app/src/ .
  3. Щелкните правой кнопкой мыши каталог src и выберите «Новый > каталог» .
  4. В меню в рамках исходных наборов Gradle выберите Full/Java .
  5. Нажмите Enter .

Android Studio создает каталог Source Set для вашего типа сборки отладки, а затем создает внутри него java/ Directory. В качестве альтернативы, Android Studio может создавать каталоги для вас, когда вы добавляете новый файл в свой проект для конкретного варианта сборки.

Например, для создания значений XML -файла для вашего типа «отладки»:

  1. На панели проекта щелкните правой кнопкой мыши каталог src и выберите New > XML > Значения XML .
  2. Введите имя для файла XML или сохраните имя по умолчанию.
  3. В меню рядом с набором целевого источника выберите отладку .
  4. Нажмите на отдел .

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

Рисунок 1.. Новые каталоги набора источников для типа сборки «отладки».

Активные наборы источников имеют зеленый индикатор в их значке, чтобы показать, что они активны. Набор источника debug суффикс с [main] , чтобы показать, что он будет объединен с main набором источника.

Используя ту же процедуру, вы также можете создавать каталоги набора источников для вкусов продуктов, таких как src/demo/ и Build Variants, такие как src/demoDebug/ . Кроме того, вы можете создать исходные наборы тестирования, которые нацелены на конкретные варианты сборки, такие как src/androidTestDemoDebug/ . Чтобы узнать больше, прочитайте о наборах исходных тестирования .

Изменить конфигурации исходного набора по умолчанию

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

Блок sourceSets должен быть в блоке android . Вам не нужно перемещать исходные файлы; Вам нужно только предоставить Gradle по пути (ы) по сравнению с файлом на уровне модуля build.gradle.kts , где Gradle может найти файлы для каждого компонента исходного набора. Чтобы узнать, какие компоненты вы можете настроить и можете ли вы сопоставить их с несколькими файлами или каталогами, см. Справочник API API API API Android Gradle .

Следующий образец кода отображает источники из app/other/ каталога в определенные компоненты main набора источников и изменяет корневой каталог исходного набора androidTest :

Котлин

android {
  ...
  // Encapsulates configurations for the main source set.
  sourceSets.getByName("main") {
    // Changes the directory for Java sources. The default directory is
    // 'src/main/java'.
    java.setSrcDirs(listOf("other/java"))

    // If you list multiple directories, Gradle uses all of them to collect
    // sources. Because Gradle gives these directories equal priority, if
    // you define the same resource in more than one directory, you receive an
    // error when merging resources. The default directory is 'src/main/res'.
    res.setSrcDirs(listOf("other/res1", "other/res2"))

    // Note: Avoid specifying a directory that is a parent to one
    // or more other directories you specify. For example, avoid the following:
    // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
    // Specify either only the root 'other/res1' directory or only the
    // nested 'other/res1/layouts' and 'other/res1/strings' directories.

    // For each source set, you can specify only one Android manifest.
    // By default, Android Studio creates a manifest for your main source
    // set in the src/main/ directory.
    manifest.srcFile("other/AndroidManifest.xml")
    ...
  }

  // Create additional blocks to configure other source sets.
  sourceSets.getByName("androidTest") {
      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
  }
}
...

Groovy

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you receive an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: Avoid specifying a directory that is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // Specify either only the root 'other/res1' directory or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

Обратите внимание, что справочный каталог может принадлежать только одному исходному набору. Например, вы не можете поделиться одними и теми же источниками тестирования с наборами test и источников androidTest . Это связано с тем, что Android Studio создает отдельные модули IntelliJ для каждого набора источников и не может поддерживать дублирующие корни контента в наборах исходных источников.

Построить с помощью исходных наборов

Вы можете использовать каталоги Source Set для содержания кода и ресурсов, которые вы хотите, упакованы только с определенными конфигурациями. Например, если вы создаете вариант сборки DemodeBug », который представляет собой поперечный продукт« демонстрационного »аромата продукта и типа« отладка », Градл смотрит на эти каталоги и дает им следующий приоритет:

  1. src/demoDebug/ (набор источника варианта сборки)
  2. src/debug/ (набор источников типа сборки)
  3. src/demo/ (Набор источника вкуса продукта)
  4. src/main/ (набор основного источника)

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

Если вы объединяете несколько ароматов продукта , приоритет между ароматами продукта определяется измерением вкуса, к которому они принадлежат. При перечислении ароматических размеров со свойством android.flavorDimensions , ароматы продуктов, которые принадлежат к первому измерению вкуса, имеют более высокий приоритет, чем те, которые принадлежат ко второму измерению вкуса, и так далее. Кроме того, исходные наборы, которые вы создаете для комбинаций ароматов продукта, имеют более высокий приоритет, чем исходные наборы, которые принадлежат индивидуальному вкусу продукта.

Приоритетный заказ определяет, какой набор источников имеет более высокий приоритет, когда Gradle объединяет код и ресурсы. Поскольку каталог demoDebug/ Source Set, вероятно, содержит файлы, которые являются специфическими для этого варианта сборки, если demoDebug/ включает файл, который также определяется в debug/ , Gradle использует файл в наборе demoDebug/ Source. Точно так же Gradle дает файлы в типе сборки, а источник вкуса продукта устанавливает более высокий приоритет, чем те же файлы в main/ . Градл рассматривает этот приоритетный заказ при применении следующих правил сборки:

  • Весь исходный код в каталогах kotlin/ или java/ Combillesmented вместе для создания одного выхода.

    ПРИМЕЧАНИЕ. Для данного варианта сборки Gradle выбрасывает ошибку сборки, если он встречается с двумя или более каталогов исходных наборов, которые определили один и тот же класс Kotlin или Java. Например, при создании приложения отладки вы не можете определить как src/debug/Utility.kt , так и src/main/Utility.kt , потому что Градл смотрит на обе эти каталоги во время процесса сборки и бросает ошибку «дубликат класса» . Если вам нужны разные версии Utility.kt для различных типов сборки, каждый тип сборки должен определить свою собственную версию файла и не включать его в набор main/ источников.

  • Манифесты объединены в один манифест. Приоритет приведен в том же порядке, что и список в предыдущем примере. То есть манифестные настройки для типа сборки переопределяют настройки манифеста для аромата продукта и так далее. Чтобы узнать больше, прочитайте о явном слиянии .
  • Файлы в values/ каталогах объединяются вместе. Если два файла имеют одинаковое имя, например, два файла strings.xml , приоритет приведен в том же порядке, что и список в предыдущем примере. То есть значения, определенные в файле в наборе источника типа сборки, переопределяют значения, определенные в одном и том же файле в аромате продукта, и так далее.
  • Ресурсы в res/ и asset/ каталоги упакованы вместе. Если есть ресурсы с тем же именем, определенным в двух или более исходных наборах, приоритет приведен в том же порядке, что и список в предыдущем примере.
  • Gradle дает ресурсы и манифесты, включенные в зависимости от библиотечного модуля, самый низкий приоритет при создании приложения.

Объявить зависимости

Чтобы настроить зависимость для конкретного варианта сборки или набора источников тестирования , префикс название варианта сборки или исходного набора тестирования перед ключевым словом Implementation , как показано в следующем примере:

Котлин

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    "freeImplementation"(project(":mylibrary"))

    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

Groovy

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    freeImplementation project(":mylibrary")

    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

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

Использовать управление зависимостями варианта

Плагин Android Gradle 3.0.0 и выше включает новый механизм зависимостей, который автоматически соответствует вариантам при употреблении библиотеки. Это означает, что вариант debug приложения автоматически потребляет вариант debug библиотеки и так далее. Он также работает при использовании вкуса: вариант приложения freeDebug будет потреблять вариант freeDebug библиотеки.

Чтобы плагин точно соответствовал вариантам, вам необходимо предоставить соответствующие запасные отступления , как описано в следующем разделе, для случаев, когда прямое совпадение невозможно.

Например, предположим, что ваше приложение настраивает тип сборки, называемый «постановка», но одна из его библиотечных зависимостей не. Когда плагин пытается создать версию «постановки» вашего приложения, он не будет знать, какую версию библиотеки использовать, и вы увидите сообщение об ошибке, похожее на следующее:

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

Разрешить ошибки сборки, связанные с сопоставлением вариантов

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

Ниже приведен список вопросов, связанных с сопоставлением зависимостей с вариантами, и как их решить с помощью свойств DSL:

  • Ваше приложение включает в себя тип сборки, которого нет библиотечной зависимости.

    Например, ваше приложение включает в себя тип «постановки» сборки, но зависимость включает только типы сборки «отладка» и «выпуск».

    Обратите внимание, что нет проблем, когда библиотечная зависимость включает в себя тип сборки, которого нет в вашем приложении. Это потому, что плагин никогда не запрашивает этот тип сборки от зависимости.

    Используйте matchingFallbacks , чтобы указать альтернативные совпадения для данного типа сборки, как показано здесь:

    Котлин

    // In the app's build.gradle.kts file.
    android {
        buildTypes {
            getByName("debug") {}
            getByName("release") {}
            create("staging") {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks += listOf("debug", "qa", "release")
            }
        }
    }

    Groovy

    // In the app's build.gradle file.
    android {
        buildTypes {
            debug {}
            release {}
            staging {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks = ['debug', 'qa', 'release']
            }
        }
    }
    
  • Для данного ароматического измерения, которое существует в приложении и в его библиотечной зависимости, ваше приложение включает в себя ароматы, которых нет в библиотеке.

    Например, как ваше приложение, так и его библиотечные зависимости включают в себя аромат «уровня». Тем не менее, измерение «уровня» в приложении включает в себя «бесплатные» и «оплачиваемые» ароматы, но зависимость включает только «демонстрацию» и «платные» ароматы для того же измерения.

    Обратите внимание, что для данного ароматического измерения, которое существует в приложении и в его библиотечной зависимости, нет проблем, когда библиотека включает в себя вкус продукта, который нет в вашем приложении. Это потому, что плагин никогда не запрашивает этот вкус от зависимости.

    Используйте matchingFallbacks , чтобы указать альтернативные совпадения для «бесплатного» аромата продукта приложения, как показано здесь:

    Котлин

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions += "tier"
        productFlavors {
            create("paid") {
                dimension = "tier"
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            create("free") {
                dimension = "tier"
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks += listOf("demo", "trial")
            }
        }
    }
    

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions 'tier'
        productFlavors {
            paid {
                dimension 'tier'
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            free {
                dimension 'tier'
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks = ['demo', 'trial']
            }
        }
    }
    
  • Библиотечная зависимость включает в себя ароматное измерение, которое нет вашего приложения.

    Например, библиотечная зависимость включает в себя ароматы для измерения «minapi», но ваше приложение включает в себя ароматы только для измерения «уровня». Если вы хотите создать версию вашего приложения «Freedebug», плагин не знает, использовать ли версия «minapi23debug» или «minapi18debug».

    Обратите внимание, что нет проблем, когда ваше приложение включает в себя ароматное измерение, которого нет библиотечной зависимости. Это потому, что плагин соответствует ароматам только тех измерений, которые существуют в зависимости. Например, если зависимость не включает измерение для ABIS, версия «freex86debug» вашего приложения будет использовать версию «Freebebug».

    Используйте missingDimensionStrategy в блоке defaultConfig , чтобы указать вкус плагина по умолчанию, чтобы выбрать из каждого пропущенного измерения, как показано в следующем образце. Вы также можете переопределить свой выбор в блоке productFlavors , поэтому каждый вкус может указать различную стратегию соответствия для недостающего измерения.

    Котлин

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy("minApi", "minApi18", "minApi23")
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy("abi", "x86", "arm64")
        }
        flavorDimensions += "tier"
        productFlavors {
            create("free") {
                dimension = "tier"
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the "minApi" dimension.
                missingDimensionStrategy("minApi", "minApi23", "minApi18")
            }
            create("paid") {}
        }
    }
    

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy 'abi', 'x86', 'arm64'
        }
        flavorDimensions 'tier'
        productFlavors {
            free {
                dimension 'tier'
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the 'minApi' dimension.
                missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
            }
            paid {}
        }
    }
    

Для получения дополнительной информации см. matchingFallbacks и missingDimensionStrategy в плагине Android Gradle DSL.

Настройте настройки подписания

Gradle не подписывает APK или AAB вашего релиза, если вы явно не определите конфигурацию подписания для этой сборки. Если у вас еще нет ключа подписи, сгенерируйте клавишу загрузки и магазин клавиш с помощью Android Studio.

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

  1. Создайте магазин ключей. Слайт ключей - это двоичный файл, который содержит набор частных ключей. Вы должны сохранить свой магазин ключей в безопасном и безопасном месте.
  2. Создайте закрытый ключ. Частный ключ используется для подписи вашего приложения для распространения и никогда не включается в приложение и не раскрывается несанкционированным третьим лицам.
  3. Добавьте конфигурацию подписания в файл на уровне модуля build.gradle.kts :

    Котлин

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            create("release") {
                storeFile = file("myreleasekey.keystore")
                storePassword = "password"
                keyAlias = "MyReleaseKey"
                keyPassword = "password"
            }
        }
        buildTypes {
            getByName("release") {
                ...
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }

    Groovy

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            release {
                storeFile file("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }

ПРИМЕЧАНИЕ. Включение паролей для вашего ключа выпуска и хранилища ключей в файл сборки не является хорошей практикой безопасности. Вместо этого настройте файл сборки, чтобы получить эти пароли из переменных среды или получить процесс сборки для этих паролей.

Чтобы получить эти пароли из переменных среды:

Котлин

storePassword = System.getenv("KSTOREPWD")
keyPassword = System.getenv("KEYPWD")

Groovy

storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")

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

После завершения этого процесса вы можете распространять свое приложение и опубликовать его в Google Play.

ПРЕДУПРЕЖДЕНИЕ: Держите свой магазин ключей и личный ключ в безопасном и безопасном месте и убедитесь, что у вас есть безопасные резервные копии. Если вы используете подписание приложения Play и потеряете ключ загрузки, вы можете запросить сброс, используя Play Console. Если вы публикуете приложение без подписания приложений Play (для приложений, созданных до августа 2021 года), и вы теряете ключ подписания приложения, вы не сможете публиковать какие -либо обновления в своем приложении, поскольку вы всегда должны подписать все версии вашего приложения с тот же ключ.

Подписание приложения для одежды ОС

При публикации приложений OS Wear ОС как Watch APK, так и опциональный телефон APK должны быть подписаны с одним и тем же ключом. Для получения дополнительной информации о приложениях для упаковки и подписи ОС см. Пакет и распространите приложения для износа .

,

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

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

Варианты сборки являются результатом Gradle с использованием определенного набора правил для объединения настроек, кода и ресурсов, настроенных в ваших типах сборки и вкусах продуктов. Хотя вы не настраиваете варианты сборки напрямую, вы настраиваете типы сборки и ароматы продукта, которые их образуют.

Например, аромат «демонстрации» может указать определенные функции и требования к устройствам, такие как пользовательский исходный код, ресурсы и минимальные уровни API, в то время как тип сборки «отладки» применяет различные настройки сборки и упаковки, такие как параметры отладки и подпись Ключи. Вариант сборки, который объединяет эти два, - это версия вашего приложения «DemodeBug», и он включает в себя комбинацию конфигураций и ресурсов, включенных в аромат «демонстрации», тип сборки «Debug» и main/ исходный набор.

Настройте типы сборки

Вы можете создавать и настраивать типы сборки внутри блока android файла на уровне модуля build.gradle.kts . Когда вы создаете новый модуль, Android Studio автоматически создает типы сборки отладки и выпуска. Хотя тип сборки отладки не отображается в файле конфигурации Build, Android Studio настраивает его с debuggable true . Это позволяет отлаживать приложение на безопасных устройствах Android и настраивает подписание приложений с помощью общего магазина ключей отладки.

Вы можете добавить тип сборки отладки в свою конфигурацию, если вы хотите добавить или изменить определенные настройки. В следующем примере указывается applicationIdSuffix для типа сборки отладки и настраивает тип сборки «постановка», который инициализируется с помощью настроек из типа сборки отладки:

Котлин

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
        ...
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }

        getByName("debug") {
            applicationIdSuffix = ".debug"
            isDebuggable = true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        create("staging") {
            initWith(getByName("debug"))
            manifestPlaceholders["hostName"] = "internal.example.com"
            applicationIdSuffix = ".debugStaging"
        }
    }
}

Groovy

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
        ...
    }
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".debug"
            debuggable true
        }

        /**
         * The `initWith` property lets you copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        staging {
            initWith debug
            manifestPlaceholders = [hostName:"internal.example.com"]
            applicationIdSuffix ".debugStaging"
        }
    }
}

Примечание. Когда вы вносите изменения в файл конфигурации сборки, Android Studio требует, чтобы вы синхронизировали свой проект с новой конфигурацией. Чтобы синхронизировать свой проект, нажмите Sync Now в панели уведомлений, которая появляется при внесении изменения или нажимает Sync Project с панели инструментов. Если Android Studio замечает какие -либо ошибки с вашей конфигурацией, окно сообщений , по -видимому, описывает проблему.

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

Настройте ароматы продукта

Создание ароматов продукта похоже на создание типов сборки. Добавьте ароматы продукта в блок productFlavors в конфигурацию вашей сборки и включите настройки, которые вы хотите. Ароматы продукта поддерживают те же свойства, что и defaultConfig , потому что defaultConfig фактически принадлежит классу ProductFlavor . Это означает, что вы можете предоставить базовую конфигурацию для всех вкусов в блоке defaultConfig , и каждый вкус может изменить любое из этих значений по умолчанию, например, applicationId . Чтобы узнать больше об идентификаторе приложения, прочтите установить идентификатор приложения .

Примечание. Вам все еще нужно указать имя пакета, используя атрибут package в файле main/ Manifest. Вы также должны использовать имя этого пакета в своем исходном коде для обозначения класса R или для разрешения любой относительной деятельности или регистрации услуг. Это позволяет использовать applicationId чтобы придать каждому продукту уникальный идентификатор для упаковки и распространения без необходимости изменения исходного кода.

Все ароматы должны принадлежать к именованному вкусовому измерению, которое представляет собой группу ароматов продукта. Вы должны назначить все ароматы в ароматный размер; В противном случае вы получите следующую ошибку сборки.

  Error: All flavors must now belong to a named flavor dimension.
  The flavor 'flavor_name' is not assigned to a flavor dimension.

Если заданный модуль указывает только одно измерение вкуса, плагин Android Gradle автоматически назначает все ароматы модуля этому измерению.

Следующий образец кода создает ароматный размер под названием «Версия» и добавляет «демонстрацию» и «полные» ароматы продукта. Эти ароматы предоставляют свои собственные applicationIdSuffix и versionNameSuffix :

Котлин

android {
    ...
    defaultConfig {...}
    buildTypes {
        getByName("debug"){...}
        getByName("release"){...}
    }
    // Specifies one flavor dimension.
    flavorDimensions += "version"
    productFlavors {
        create("demo") {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension = "version"
            applicationIdSuffix = ".demo"
            versionNameSuffix = "-demo"
        }
        create("full") {
            dimension = "version"
            applicationIdSuffix = ".full"
            versionNameSuffix = "-full"
        }
    }
}

Groovy

android {
    ...
    defaultConfig {...}
    buildTypes {
        debug{...}
        release{...}
    }
    // Specifies one flavor dimension.
    flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}

ПРИМЕЧАНИЕ. Если у вас есть приложение Legacy, созданное до августа 2021 года), которое вы распространяете с помощью APK в Google Play, для распространения вашего приложения с помощью нескольких поддержки APK в Google Play, назначить одно и то же значение applicationId ко всем вариантам и предоставить каждому варианту другой versionCode . Чтобы распространять различные варианты вашего приложения в качестве отдельных приложений в Google Play, вам необходимо назначить различные applicationId для каждого варианта.

После того, как вы создаете и настроите ароматы продукта, нажмите Sync Now в панели уведомлений. После завершения синхронизации Gradle автоматически создает варианты сборки на основе ваших типов сборки и ароматов продукта и называет их в соответствии с <product-flavor><Build-Type> . Например, если вы создали «Демо -демонстрационные» и «полные» ароматы продукта и сохранили типы сборки «отладка» и «Выпуск», Gradle создает следующие варианты сборки:

  • demoDebug
  • demoRelease
  • fullDebug
  • fullRelease

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

Измените идентификатор приложения для вариантов сборки

Когда вы создаете APK или AAB для вашего приложения, инструменты сборки помечает приложение с идентификатором приложения, определенным в блоке defaultConfig из файла build.gradle.kts , как показано в следующем примере. Однако, если вы хотите создать разные версии вашего приложения, чтобы отображаться в виде отдельных списков в Google Play Store, таких как версия «бесплатно» и «Pro», вам необходимо создать отдельные варианты сборки , каждый из которых имеет другой идентификатор приложения.

В этом случае определите каждый вариант сборки как отдельный вкус продукта . Для каждого вкуса внутри блока productFlavors вы можете переопределить свойство applicationId , или вместо этого вы можете добавить сегмент к идентификатору приложения по умолчанию, используя applicationIdSuffix , как показано здесь:

Котлин

android {
    defaultConfig {
        applicationId = "com.example.myapp"
    }
    productFlavors {
        create("free") {
            applicationIdSuffix = ".free"
        }
        create("pro") {
            applicationIdSuffix = ".pro"
        }
    }
}

Groovy

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

Таким образом, идентификатор приложения для «бесплатного» аромата продукта является «com.example.myapp.free».

Вы также можете использовать applicationIdSuffix для добавления сегмента на основе вашего типа сборки , как показано здесь:

Котлин

android {
    ...
    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
        }
    }
}

Groovy

android {
    ...
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }
    }
}

Поскольку Gradle применяет конфигурацию типа сборки после аромата продукта, идентификатор приложения для варианта сборки «Бесплатная отладка» - "com.example.myapp.free.debug". Это полезно, когда вы хотите иметь как отладку, так и настройку выпуска на одном и том же устройстве, потому что никакие два приложения не могут иметь одинакового идентификатора приложения.

Если у вас есть устаревшее приложение (созданное до августа 2021 года), которое вы распространяете с помощью APK в Google Play, и вы хотите использовать один и тот же список приложений для распределения нескольких APK, которые нацелены на различную конфигурацию устройства, такие как уровень API, тогда вы Должен использовать один и тот же идентификатор приложения для каждого варианта сборки, но дайте каждому APK различный versionCode . Для получения дополнительной информации прочитайте о нескольких поддержке APK . Публикация с использованием AABS не зависит от того, что он использует один артефакт, который по умолчанию использует один код версии и идентификатор приложения.

Совет: если вам нужно ссылаться на идентификатор приложения в вашем манифестном файле, вы можете использовать заполнитель ${applicationId} в любом атрибуте Manifest. Во время сборки Градл заменяет этот тег фактическим идентификатором приложения. Для получения дополнительной информации см. Переменные ввода в манифест .

Объедините несколько ароматов продукта с ароматными размерами

В некоторых случаях вы можете объединить конфигурации из нескольких ароматов продукта. Например, вы можете создать различные конфигурации для «полных» и «демонстрационных» вкусов продукта, которые основаны на уровне API. Для этого плагин Android Gradle позволяет создавать несколько групп ароматов продуктов в качестве ароматных размеров.

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

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

Котлин

android {
  ...
  buildTypes {
    getByName("debug") {...}
    getByName("release") {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions += listOf("api", "mode")

  productFlavors {
    create("demo") {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension = "mode"
      ...
    }

    create("full") {
      dimension = "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    create("minApi24") {
      dimension = "api"
      minSdk = 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.
      versionCode = 30000 + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi24"
      ...
    }

    create("minApi23") {
      dimension = "api"
      minSdk = 23
      versionCode = 20000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi23"
      ...
    }

    create("minApi21") {
      dimension = "api"
      minSdk = 21
      versionCode = 10000  + (android.defaultConfig.versionCode ?: 0)
      versionNameSuffix = "-minApi21"
      ...
    }
  }
}
...

Groovy

android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list the dimensions determines their priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property, with the first dimension having a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion 24
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level.

      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion 23
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion 21
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

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

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

  • Вариант сборки: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
  • Соответствующий APK: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
  • Например,
    Вариант сборки: minApi24DemoDebug
    Соответствующий APK: app-minApi24-demo-debug.apk

В дополнение к каталогам исходных наборов, которые вы можете создать для каждого отдельного варианта продукта и создания, вы также можете создавать каталоги наборов источников для каждой комбинации вкусов продукта. Например, вы можете создавать и добавлять источники Java в src/demoMinApi24/java/ Directory, и Gradle использует эти источники только при создании варианта, который сочетает в себе эти два вкуса продукта.

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

Фильтрующие варианты

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

Используя конфигурацию сборки из предыдущего раздела в качестве примера, предположим, что вы планируете поддерживать только уровни API 23 и выше для демонстрационной версии приложения. Вы можете использовать блок variantFilter , чтобы отфильтровать все конфигурации вариантов сборки, которые объединяют ароматы «minapi21» и «демо»:

Котлин

android {
  ...
  buildTypes {...}

  flavorDimensions += listOf("api", "mode")
  productFlavors {
    create("demo") {...}
    create("full") {...}
    create("minApi24") {...}
    create("minApi23") {...}
    create("minApi21") {...}
  }
}

androidComponents {
    beforeVariants { variantBuilder ->
        // To check for a certain build type, use variantBuilder.buildType == "<buildType>"
        if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
            // Gradle ignores any variants that satisfy the conditions above.
            variantBuilder.enable = false
        }
    }
}
...

Groovy

android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

После того, как вы добавите вариант фильтра в свою конфигурацию сборки и нажмите Sync Now в панели уведомлений, Gradle игнорирует любые варианты сборки, которые соответствуют указанным условиям. The build variants no longer appear in the menu when you click Build > Select Build Variant from the menu bar or Build Variants in the tool window bar.

Create source sets

By default, Android Studio creates the main/ source set and directories for everything you want to share between all your build variants. However, you can create new source sets to control exactly which files Gradle compiles and packages for specific build types, product flavors, combinations of product flavors (when using flavor dimensions ), and build variants.

For example, you can define basic functionality in the main/ source set and use product flavor source sets to change the branding of your app for different clients, or include special permissions and logging functionality only for build variants that use the debug build type.

Gradle expects source set files and directories to be organized in a certain way, similar to the main/ source set. For example, Gradle expects Kotlin or Java class files that are specific to your "debug" build type to be located in the src/debug/kotlin/ or src/debug/java/ directories.

The Android Gradle plugin provides a useful Gradle task that shows you how to organize your files for each of your build types, product flavors, and build variants. For example, the following sample from the task output describes where Gradle expects to find certain files for the "debug" build type:

------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: debugCompile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Kotlin sources: [app/src/debug/kotlin, app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

To view this output, proceed as follows:

  1. Click Gradle in the tool window bar.
  2. Navigate to MyApplication > Tasks > android and double-click sourceSets .

    To see the Tasks folder, you must let Gradle build the task list during sync. To do so, follow these steps:

    1. Click File > Settings > Experimental ( Android Studio > Settings > Experimental on macOS).
    2. Deselect Do not build Gradle task list during Gradle sync .
  3. After Gradle executes the task, the Run window opens to display the output.

Note: The task output also shows you how to organize source sets for files you want to use to run tests for your app, such as the test/ and androidTest/ testing source sets .

When you create a new build variant, Android Studio doesn't create the source set directories for you, but it does give you a few options to help you. For example, to create just the java/ directory for your "debug" build type:

  1. Open the Project pane and select the Project view from the menu at the top of the pane.
  2. Navigate to MyProject/app/src/ .
  3. Right-click the src directory and select New > Directory .
  4. From the menu under Gradle Source Sets , select full/java .
  5. Press Enter .

Android Studio creates a source set directory for your debug build type and then creates the java/ directory inside it. Alternatively, Android Studio can create the directories for you when you add a new file to your project for a specific build variant.

For example, to create a values XML file for your "debug" build type:

  1. In the Project pane, right-click the src directory and select New > XML > Values XML File .
  2. Enter the name for the XML file or keep the default name.
  3. From the menu next to Target Source Set , select debug .
  4. Click Finish .

Because the "debug" build type was specified as the target source set, Android Studio automatically creates the necessary directories when it creates the XML file. The resulting directory structure looks like figure 1.

Figure 1. New source set directories for the "debug" build type.

Active source sets have a green indicator in their icon to show that they are active. The debug source set is suffixed with [main] to show that it will be merged in to the main source set.

Using the same procedure, you can also create source set directories for product flavors, such as src/demo/ , and build variants, such as src/demoDebug/ . Additionally, you can create testing source sets that target specific build variants, such as src/androidTestDemoDebug/ . To learn more, read about testing source sets .

Change default source set configurations

If you have sources that are not organized into the default source set file structure that Gradle expects, as described in the previous section about creating source sets , you can use the sourceSets block to change where Gradle looks to gather files for each component of a source набор.

The sourceSets block must be in the android block. You don't need to relocate the source files; you only need to provide Gradle with the path(s), relative to the module-level build.gradle.kts file, where Gradle can find files for each source set component. To learn which components you can configure and whether you can map them to multiple files or directories, see the Android Gradle plugin API reference .

The following code sample maps sources from the app/other/ directory to certain components of the main source set and changes the root directory of the androidTest source set:

Kotlin

android {
  ...
  // Encapsulates configurations for the main source set.
  sourceSets.getByName("main") {
    // Changes the directory for Java sources. The default directory is
    // 'src/main/java'.
    java.setSrcDirs(listOf("other/java"))

    // If you list multiple directories, Gradle uses all of them to collect
    // sources. Because Gradle gives these directories equal priority, if
    // you define the same resource in more than one directory, you receive an
    // error when merging resources. The default directory is 'src/main/res'.
    res.setSrcDirs(listOf("other/res1", "other/res2"))

    // Note: Avoid specifying a directory that is a parent to one
    // or more other directories you specify. For example, avoid the following:
    // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
    // Specify either only the root 'other/res1' directory or only the
    // nested 'other/res1/layouts' and 'other/res1/strings' directories.

    // For each source set, you can specify only one Android manifest.
    // By default, Android Studio creates a manifest for your main source
    // set in the src/main/ directory.
    manifest.srcFile("other/AndroidManifest.xml")
    ...
  }

  // Create additional blocks to configure other source sets.
  sourceSets.getByName("androidTest") {
      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot("src/tests")
      ...
  }
}
...

Groovy

android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you receive an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: Avoid specifying a directory that is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // Specify either only the root 'other/res1' directory or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

Note that a source directory can only belong to one source set. For example, you can't share the same test sources with both the test and androidTest source sets. This is because Android Studio creates separate IntelliJ modules for each source set and can't support duplicate content roots across source sets.

Build with source sets

You can use source set directories to contain the code and resources you want packaged only with certain configurations. For example, if you are building the "demoDebug" build variant, which is the crossproduct of a "demo" product flavor and "debug" build type, Gradle looks at these directories and gives them the following priority:

  1. src/demoDebug/ (build variant source set)
  2. src/debug/ (build type source set)
  3. src/demo/ (product flavor source set)
  4. src/main/ (main source set)

Source sets created for combinations of product flavors must include all flavor dimensions. For example, the build variant source set must be the combination of the build type and all flavor dimensions. Merging code and resources involving folders that cover multiple but not all flavor dimensions is not supported.

If you combine multiple product flavors , priority between the product flavors is determined by the flavor dimension they belong to. When listing flavor dimensions with the android.flavorDimensions property, product flavors that belong to the first flavor dimension you list have a higher priority than those belonging to the second flavor dimension, and so on. Additionally, source sets you create for combinations of product flavors have a higher priority than source sets that belong to an individual product flavor.

The priority order determines which source set has a higher priority when Gradle combines code and resources. Because the demoDebug/ source set directory likely contains files that are specific to that build variant, if demoDebug/ includes a file that is also defined in debug/ , Gradle uses the file in the demoDebug/ source set. Similarly, Gradle gives files in the build type and product flavor source sets a higher priority than the same files in main/ . Gradle considers this priority order when applying the following build rules:

  • All source code in the kotlin/ or java/ directories are compiled together to generate a single output.

    Note: For a given build variant, Gradle throws a build error if it encounters two or more source set directories that have defined the same Kotlin or Java class. For example, when building a debug app, you can't define both src/debug/Utility.kt and src/main/Utility.kt , because Gradle looks at both these directories during the build process and throws a "duplicate class" error . If you want different versions of Utility.kt for different build types, each build type must define its own version of the file and not include it in the main/ source set.

  • Manifests are merged together into a single manifest. Priority is given in the same order as the list in the previous example. That is, manifest settings for a build type override the manifest settings for a product flavor, and so on. To learn more, read about manifest merging .
  • Files in the values/ directories are merged together. If two files have the same name, such as two strings.xml files, priority is given in the same order as the list in the previous example. That is, values defined in a file in the build type source set override the values defined in the same file in a product flavor, and so on.
  • Resources in the res/ and asset/ directories are packaged together. If there are resources with the same name defined in two or more source sets, priority is given in the same order as the list in the previous example.
  • Gradle gives resources and manifests included with library module dependencies the lowest priority when building the app.

Declare dependencies

To configure a dependency for a specific build variant or testing source set , prefix the name of the build variant or testing source set before the Implementation keyword, as shown in the following example:

Kotlin

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    "freeImplementation"(project(":mylibrary"))

    // Adds a remote binary dependency only for local tests.
    testImplementation("junit:junit:4.12")

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation("com.android.support.test.espresso:espresso-core:3.6.1")
}

Groovy

dependencies {
    // Adds the local "mylibrary" module as a dependency to the "free" flavor.
    freeImplementation project(":mylibrary")

    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.6.1'
}

For more information about configuring dependencies, see Add build dependencies .

Use variant-aware dependency management

The Android Gradle plugin 3.0.0 and higher includes a new dependency mechanism that automatically matches variants when consuming a library. This means an app's debug variant automatically consumes a library's debug variant, and so on. It also works when using flavors: an app's freeDebug variant will consume a library's freeDebug variant.

For the plugin to accurately match variants, you need to provide matching fallbacks as described in the following section, for instances where a direct match is not possible.

For example, suppose your app configures a build type called "staging", but one of its library dependencies doesn't. When the plugin tries to build the "staging" version of your app, it won't know which version of the library to use, and you'll see an error message similar to the following:

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

Resolve build errors related to variant matching

The plugin includes DSL elements to help you control how Gradle resolves situations in which a direct variant match between an app and a dependency is not possible.

The following is a list of issues related to variant-aware dependency matching and how to solve them using DSL properties:

  • Your app includes a build type that a library dependency does not.

    For example, your app includes a "staging" build type, but a dependency includes only "debug" and "release" build types.

    Note that there is no issue when a library dependency includes a build type that your app doesn't. That's because the plugin never requests that build type from the dependency.

    Use matchingFallbacks to specify alternative matches for a given build type, as shown here:

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        buildTypes {
            getByName("debug") {}
            getByName("release") {}
            create("staging") {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks += listOf("debug", "qa", "release")
            }
        }
    }

    Groovy

    // In the app's build.gradle file.
    android {
        buildTypes {
            debug {}
            release {}
            staging {
                // Specifies a sorted list of fallback build types that the
                // plugin can try to use when a dependency does not include a
                // "staging" build type. You may specify as many fallbacks as you
                // like, and the plugin selects the first build type that's
                // available in the dependency.
                matchingFallbacks = ['debug', 'qa', 'release']
            }
        }
    }
    
  • For a given flavor dimension that exists in both the app and its library dependency, your app includes flavors that the library does not.

    For example, both your app and its library dependencies include a "tier" flavor dimension. However, the "tier" dimension in the app includes "free" and "paid" flavors, but a dependency includes only "demo" and "paid" flavors for the same dimension.

    Note that for a given flavor dimension that exists in both the app and its library dependencies, there is no issue when a library includes a product flavor that your app does not. That's because the plugin never requests that flavor from the dependency.

    Use matchingFallbacks to specify alternative matches for the app's "free" product flavor, as shown here:

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions += "tier"
        productFlavors {
            create("paid") {
                dimension = "tier"
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            create("free") {
                dimension = "tier"
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks += listOf("demo", "trial")
            }
        }
    }
    

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Don't configure matchingFallbacks in the defaultConfig block.
        // Instead, specify fallbacks for a given product flavor in the
        // productFlavors block, as shown below.
      }
        flavorDimensions 'tier'
        productFlavors {
            paid {
                dimension 'tier'
                // Because the dependency already includes a "paid" flavor in its
                // "tier" dimension, you don't need to provide a list of fallbacks
                // for the "paid" flavor.
            }
            free {
                dimension 'tier'
                // Specifies a sorted list of fallback flavors that the plugin
                // can try to use when a dependency's matching dimension does
                // not include a "free" flavor. Specify as many
                // fallbacks as you like; the plugin selects the first flavor
                // that's available in the dependency's "tier" dimension.
                matchingFallbacks = ['demo', 'trial']
            }
        }
    }
    
  • A library dependency includes a flavor dimension that your app does not.

    For example, a library dependency includes flavors for a "minApi" dimension, but your app includes flavors for only the "tier" dimension. When you want to build the "freeDebug" version of your app, the plugin doesn't know whether to use the "minApi23Debug" or "minApi18Debug" version of the dependency.

    Note that there is no issue when your app includes a flavor dimension that a library dependency doesn't. That's because the plugin matches flavors of only the dimensions that exist in the dependency. For example, if a dependency does not include a dimension for ABIs, the "freeX86Debug" version of your app would use the "freeDebug" version of the dependency.

    Use missingDimensionStrategy in the defaultConfig block to specify the default flavor for the plugin to select from each missing dimension, as shown in the following sample. You can also override your selections in the productFlavors block, so each flavor can specify a different matching strategy for a missing dimension.

    Kotlin

    // In the app's build.gradle.kts file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy("minApi", "minApi18", "minApi23")
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy("abi", "x86", "arm64")
        }
        flavorDimensions += "tier"
        productFlavors {
            create("free") {
                dimension = "tier"
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the "minApi" dimension.
                missingDimensionStrategy("minApi", "minApi23", "minApi18")
            }
            create("paid") {}
        }
    }
    

    Groovy

    // In the app's build.gradle file.
    android {
        defaultConfig{
        // Specifies a sorted list of flavors that the plugin can try to use from
        // a given dimension. This tells the plugin to select the "minApi18" flavor
        // when encountering a dependency that includes a "minApi" dimension.
        // You can include additional flavor names to provide a
        // sorted list of fallbacks for the dimension.
        missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
        // Specify a missingDimensionStrategy property for each
        // dimension that exists in a local dependency but not in your app.
        missingDimensionStrategy 'abi', 'x86', 'arm64'
        }
        flavorDimensions 'tier'
        productFlavors {
            free {
                dimension 'tier'
                // You can override the default selection at the product flavor
                // level by configuring another missingDimensionStrategy property
                // for the 'minApi' dimension.
                missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
            }
            paid {}
        }
    }
    

For more information, see matchingFallbacks and missingDimensionStrategy in the Android Gradle plugin DSL reference.

Configure signing settings

Gradle doesn't sign your release build's APK or AAB unless you explicitly define a signing configuration for this build. If you don't have a signing key yet, generate an upload key and keystore using Android Studio.

To manually configure the signing configurations for your release build type using Gradle build configurations:

  1. Create a keystore. A keystore is a binary file that contains a set of private keys. You must keep your keystore in a safe and secure place.
  2. Create a private key. A private key is used to sign your app for distribution and is never included with the app or disclosed to unauthorized third parties.
  3. Add the signing configuration to the module-level build.gradle.kts file:

    Kotlin

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            create("release") {
                storeFile = file("myreleasekey.keystore")
                storePassword = "password"
                keyAlias = "MyReleaseKey"
                keyPassword = "password"
            }
        }
        buildTypes {
            getByName("release") {
                ...
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }

    Groovy

    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            release {
                storeFile file("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }

Note: Including the passwords for your release key and keystore inside the build file is not a good security practice. Instead, configure the build file to obtain these passwords from environment variables or have the build process prompt you for these passwords.

To obtain these passwords from environment variables:

Kotlin

storePassword = System.getenv("KSTOREPWD")
keyPassword = System.getenv("KEYPWD")

Groovy

storePassword System.getenv("KSTOREPWD")
keyPassword System.getenv("KEYPWD")

Alternatively, you can load the keystore from a local properties file. For security reasons, don't add this file to source control. Instead, set it up locally for each developer. To learn more, read Remove signing information from your build files .

After you complete this process, you can distribute your app and publish it on Google Play.

Warning: Keep your keystore and private key in a safe and secure place, and ensure that you have secure backups of them. If you use Play App Signing and you lose your upload key, you can request a reset using the Play Console. If you are publishing an app without Play App Signing (for apps created before August 2021) and you lose your app signing key, you will not be able to publish any updates to your app, since you must always sign all versions of your app with the same key.

Signing Wear OS apps

When publishing Wear OS apps, both the watch APK and optional phone APK must be signed with the same key. For more information on packaging and signing Wear OS apps, see Package and distribute Wear apps .