Управление файлами манифеста

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

Объединение нескольких файлов манифеста

Ваш файл APK или Android App Bundle может содержать только один файл AndroidManifest.xml , но ваш проект Android Studio может содержать несколько файлов манифеста, предоставленных основным набором исходных кодов, вариантами сборки и импортированными библиотеками. При сборке вашего приложения сборка Gradle объединяет все файлы манифеста в один файл манифеста, который упаковывается в ваше приложение.

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

Совет: Используйте представление объединенного манифеста , описанное в следующем разделе, чтобы просмотреть результаты объединенного манифеста и найти конфликтные ошибки.

Объединить приоритеты

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

Рисунок 1. Процесс объединения трех файлов манифеста, самого низкого приоритета, в самый высокий.

Существует три основных типа файлов манифеста, которые можно объединять друг с другом, и их приоритеты слияния следующие (сначала высший приоритет):

  1. Файл манифеста для вашего варианта сборки

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

    • Создайте вариант манифеста (например, src/demoDebug/ ).
    • Манифест типа сборки (например, src/debug/ )
    • Манифест вкуса продукта (например, src/demo/ )

      Если вы используете дополнительные измерения, приоритеты манифеста соответствуют порядку, в котором каждое измерение указано в свойстве flavorDimensions (первым является наивысший приоритет).

  2. Основной файл манифеста для модуля приложения
  3. Файл манифеста из включенной библиотеки

    Если у вас несколько библиотек, их приоритеты манифеста соответствуют порядку, в котором они отображаются в вашем блоке dependencies Gradle.

Например, манифест библиотеки объединяется с основным манифестом, а затем основной манифест объединяется с манифестом варианта сборки. Обратите внимание, что это одни и те же приоритеты слияния для всех исходных наборов, как описано в разделе Сборка с исходными наборами .

Важно: Конфигурации сборки из файла build.gradle переопределяют любые соответствующие атрибуты в объединенном файле манифеста. Например, minSdk из файла build.gradle или build.gradle.kts переопределяет соответствующий атрибут в элементе манифеста <uses-sdk> . Чтобы избежать путаницы, оставьте элемент <uses-sdk> и определите эти свойства только в файле build.gradle . Дополнительные сведения см. в разделе Настройка сборки .

Объединение эвристик конфликтов

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

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

В таблице 1 показаны возможные результаты, когда инструмент слияния пытается объединить все атрибуты в один и тот же элемент.

Таблица 1. Поведение слияния по умолчанию для значений атрибутов

Высокоприоритетный атрибут Атрибут с низким приоритетом Объединенный результат атрибута
Нет значения Нет значения Нет значения (используйте значение по умолчанию)
Значение Б Значение Б
Значение А Нет значения Значение А
Значение А Значение А
Значение Б Ошибка конфликта — необходимо добавить маркер правила слияния .

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

  • Атрибуты элемента <manifest> никогда не объединяются; используются только атрибуты из манифеста с наивысшим приоритетом.
  • Атрибут android:required в элементах <uses-feature> и <uses-library> использует слияние OR . В случае конфликта применяется "true" и всегда включается функция или библиотека, требуемая одним манифестом.
  • Атрибуты в элементе <uses-sdk> всегда используют значения из манифеста с более высоким приоритетом, за исключением следующих ситуаций:
    • Если манифест с более низким приоритетом имеет более высокое значение minSdk , возникает ошибка, если вы не примените правило слияния overrideLibrary .
    • Если манифест с более низким приоритетом имеет более низкое значение targetSdkVersion , инструмент слияния использует значение из манифеста с более высоким приоритетом, а также добавляет любые системные разрешения, необходимые для обеспечения правильного функционирования импортированной библиотеки (для случаев в более поздняя версия Android имеет увеличенные ограничения разрешений). Дополнительные сведения об этом поведении см. в разделе о неявных системных разрешениях .
  • Элемент <intent-filter> никогда не сопоставляется между манифестами. Каждый из них считается уникальным и добавляется к общему родительскому элементу в объединенном манифесте.

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

Не зависьте от значений атрибутов по умолчанию. Поскольку все уникальные атрибуты объединяются в один и тот же элемент, это может привести к неожиданным результатам, если манифест с более высоким приоритетом действительно зависит от значения атрибута по умолчанию, не объявляя его. Например, если манифест с более высоким приоритетом не объявляет атрибут android:launchMode , то он использует значение по умолчанию "standard" , но если манифест с более низким приоритетом объявляет этот атрибут с другим значением, это значение применяется к объединенный манифест, который переопределяет значение по умолчанию. Вы должны явно определить каждый атрибут так, как вы хотите. Значения по умолчанию для каждого атрибута описаны в справочнике по манифесту .

Объединение маркеров правил

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

При объединении двух файлов манифеста инструмент слияния ищет эти маркеры в файле манифеста с более высоким приоритетом.

Все маркеры принадлежат пространству имен tools Android, поэтому сначала необходимо объявить это пространство имен в элементе <manifest> , как показано здесь:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp"
    xmlns:tools="http://schemas.android.com/tools">

Маркеры узлов

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

tools:node="merge"
Объедините все атрибуты в этом теге и все вложенные элементы при отсутствии конфликтов, используя эвристику конфликтов слияния . Это поведение элементов по умолчанию.

Манифест с низким приоритетом:

<activity android:name="com.example.ActivityOne"
    android:windowSoftInputMode="stateUnchanged">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Высокоприоритетный манифест:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait"
    tools:node="merge">
</activity>

Результат объединенного манифеста:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateUnchanged">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
tools:node="merge-only-attributes"
Объединять атрибуты только в этом теге; не объединяйте вложенные элементы.

Манифест с низким приоритетом:

<activity android:name="com.example.ActivityOne"
    android:windowSoftInputMode="stateUnchanged">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <data android:type="image/*" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Высокоприоритетный манифест:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait"
    tools:node="merge-only-attributes">
</activity>

Результат объединенного манифеста:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateUnchanged">
</activity>
tools:node="remove"
Удалите этот элемент из объединенного манифеста. Используется, когда вы обнаруживаете в объединенном манифесте элемент, который вам не нужен и который был предоставлен файлом манифеста с более низким приоритетом, который находится вне вашего контроля (например, импортированной библиотекой).

Манифест с низким приоритетом:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="cow"
      android:value="@string/moo"/>
  <meta-data android:name="duck"
      android:value="@string/quack"/>
</activity-alias>

Высокоприоритетный манифест:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="cow"
      tools:node="remove"/>
</activity-alias>

Результат объединенного манифеста:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="duck"
      android:value="@string/quack"/>
</activity-alias>
tools:node="removeAll"
Аналогично tools:node="remove" , но удаляет все элементы, соответствующие этому типу элемента (внутри одного родительского элемента).

Манифест с низким приоритетом:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="cow"
      android:value="@string/moo"/>
  <meta-data android:name="duck"
      android:value="@string/quack"/>
</activity-alias>

Высокоприоритетный манифест:

<activity-alias android:name="com.example.alias">
  <meta-data tools:node="removeAll"/>
</activity-alias>

Результат объединенного манифеста:

<activity-alias android:name="com.example.alias">
</activity-alias>
tools:node="replace"
Полностью замените элемент с более низким приоритетом. То есть, если в манифесте с более низким приоритетом есть соответствующий элемент, игнорируйте его и используйте этот элемент точно так, как он отображается в этом манифесте.

Манифест с низким приоритетом:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="cow"
      android:value="@string/moo"/>
  <meta-data android:name="duck"
      android:value="@string/quack"/>
</activity-alias>

Высокоприоритетный манифест:

<activity-alias android:name="com.example.alias"
    tools:node="replace">
  <meta-data android:name="fox"
      android:value="@string/dingeringeding"/>
</activity-alias>

Результат объединенного манифеста:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="fox"
      android:value="@string/dingeringeding"/>
</activity-alias>
tools:node="strict"
Генерировать сбой сборки каждый раз, когда этот элемент в манифесте с более низким приоритетом не совсем соответствует элементу в манифесте с более высоким приоритетом (если только это не разрешено другими маркерами правила слияния). Это переопределяет эвристику конфликта слияния . Например, если манифест с более низким приоритетом включает дополнительный атрибут, сборка завершается неудачей (тогда как поведение по умолчанию добавляет дополнительный атрибут в объединенный манифест).

Манифест с низким приоритетом:

<activity android:name="com.example.ActivityOne"
    android:windowSoftInputMode="stateUnchanged">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Высокоприоритетный манифест:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait"
    tools:node="strict">
</activity>

Это создает ошибку слияния манифеста. Два элемента манифеста вообще не могут различаться в строгом режиме. Чтобы устранить эти различия, необходимо применить другие маркеры правила слияния. (Без tools:node="strict" эти два файла могут объединиться без ошибок, как показано в примере tools:node="merge" .)

Маркеры атрибутов

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

tools:remove=" attr, ... "
Удалите указанные атрибуты из объединенного манифеста. Используется, когда файл манифеста с более низким приоритетом содержит эти атрибуты и вы хотите, чтобы они не попали в объединенный манифест.

Манифест с низким приоритетом:

<activity android:name="com.example.ActivityOne"
    android:windowSoftInputMode="stateUnchanged">

Высокоприоритетный манифест:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait"
    tools:remove="android:windowSoftInputMode">

Результат объединенного манифеста:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait">
tools:replace=" attr, ... "
Замените указанные атрибуты в манифесте с более низким приоритетом атрибутами из этого манифеста. Другими словами, всегда сохраняйте значения манифеста с более высоким приоритетом.

Манифест с низким приоритетом:

<activity android:name="com.example.ActivityOne"
    android:theme="@oldtheme"
    android:exported="false"
    android:windowSoftInputMode="stateUnchanged">

Высокоприоритетный манифест:

<activity android:name="com.example.ActivityOne"
    android:theme="@newtheme"
    android:exported="true"
    android:screenOrientation="portrait"
    tools:replace="android:theme,android:exported">

Результат объединенного манифеста:

<activity android:name="com.example.ActivityOne"
    android:theme="@newtheme"
    android:exported="true"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateUnchanged">
tools:strict=" attr, ... "
Создавать сбой сборки каждый раз, когда эти атрибуты в манифесте с более низким приоритетом не совсем соответствуют атрибутам в манифесте с более высоким приоритетом. Это поведение по умолчанию для всех атрибутов, за исключением атрибутов со специальным поведением, описанным в эвристике конфликта слияния .

Манифест с низким приоритетом:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="landscape">
</activity>

Высокоприоритетный манифест:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait"
    tools:strict="android:screenOrientation">
</activity>

Это создает ошибку слияния манифеста. Для разрешения конфликта необходимо применить другие маркеры правила слияния. Это поведение по умолчанию, поэтому тот же результат получается при явном добавлении tools:strict="screenOrientation" .

Вы также можете применить несколько маркеров к одному элементу, как показано в следующем примере:

Манифест с низким приоритетом:

<activity android:name="com.example.ActivityOne"
    android:theme="@oldtheme"
    android:exported="false"
    android:allowTaskReparenting="true"
    android:windowSoftInputMode="stateUnchanged">

Высокоприоритетный манифест:

<activity android:name="com.example.ActivityOne"
    android:theme="@newtheme"
    android:exported="true"
    android:screenOrientation="portrait"
    tools:replace="android:theme,android:exported"
    tools:remove="android:windowSoftInputMode">

Результат объединенного манифеста:

<activity android:name="com.example.ActivityOne"
    android:theme="@newtheme"
    android:exported="true"
    android:allowTaskReparenting="true"
    android:screenOrientation="portrait">

Селектор маркеров

Если вы хотите применить маркеры правила слияния только к определенной импортированной библиотеке, добавьте tools:selector с именем пакета библиотеки.

Например, в следующем манифесте правило remove слияния применяется только в том случае, если файл манифеста с более низким приоритетом находится в библиотеке com.example.lib1 :

<permission android:name="permissionOne"
    tools:node="remove"
    tools:selector="com.example.lib1">

Если манифест с более низким приоритетом получен из любого другого источника, правило remove слияния игнорируется.

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

Переопределить <uses-sdk> для импортированных библиотек.

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

Чтобы инструмент слияния игнорировал этот конфликт и импортировал библиотеку, сохраняя при этом меньшее значение minSdk вашего приложения, добавьте атрибут overrideLibrary в тег <uses-sdk> . Значением атрибута может быть одно или несколько имен пакетов библиотек (разделенных запятыми), указывающих библиотеки, которые могут переопределить minSdk основного манифеста.

Например, если основной манифест вашего приложения применяет overrideLibrary следующим образом:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.app"
          xmlns:tools="http://schemas.android.com/tools">
  <uses-sdk tools:overrideLibrary="com.example.lib1, com.example.lib2"/>
...

Тогда следующий манифест можно будет объединить без ошибок в теге <uses-sdk> , а объединенный манифест сохранит minSdk="2" из манифеста приложения.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.lib1">
   <uses-sdk android:minSdk="4" />
...

Неявные системные разрешения

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

Чтобы избежать поломки приложений, которые ожидают доступа к этим API, последние версии Android позволяют приложениям продолжать получать доступ к этим API без разрешения, если targetSdkVersion установлено значение меньше, чем в версии, в которую было добавлено ограничение. Такое поведение предоставляет приложению неявное разрешение на доступ к API. Это может повлиять на объединенные манифесты с разными значениями targetSdkVersion .

Если файл манифеста с более низким приоритетом имеет более низкое значение для targetSdkVersion , которое предоставляет ему неявное разрешение, а манифест с более высоким приоритетом не имеет такого же неявного разрешения (поскольку его targetSdkVersion равна или выше, чем версия, в которой ограничение было добавлено), то инструмент слияния явно добавляет системное разрешение в объединенный манифест.

Например, если ваше приложение устанавливает targetSdkVersion значение 4 или выше и импортирует библиотеку, для которой targetSdkVersion установлено значение 3 или ниже, инструмент слияния добавляет разрешение WRITE_EXTERNAL_STORAGE в объединенный манифест.

В таблице 2 перечислены все возможные разрешения, которые можно добавить в объединенный манифест:

Таблица 2. Список разрешений, которые инструмент слияния может добавить в объединенный манифест

Манифест с более низким приоритетом объявляет Разрешения добавлены в объединенный манифест
targetSdkVersion – 3 или ниже. WRITE_EXTERNAL_STORAGE , READ_PHONE_STATE
targetSdkVersion – 15 или ниже и используется READ_CONTACTS READ_CALL_LOG
targetSdkVersion – 15 или ниже и используется WRITE_CONTACTS WRITE_CALL_LOG

Проверьте объединенный манифест и найдите конфликты.

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

  1. В Android Studio откройте файл AndroidManifest.xml .
  2. Откройте вкладку «Объединенный манифест» в нижней части редактора.

Представление «Объединенный манифест» показывает результаты объединенного манифеста слева и информацию о каждом объединенном файле манифеста справа, как показано на рис. 2.

Элементы, которые были объединены из файлов манифеста с более низким приоритетом, выделяются разными цветами слева. Ключ для каждого цвета указан в разделе «Источники манифеста» .

Рисунок 2. Представление объединенного манифеста.

Файлы манифеста, которые были частью сборки, но не содержали элементов или атрибутов, перечислены в разделе «Другие файлы манифеста» .

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

Если возникают какие-либо конфликты, они отображаются в разделе «Ошибки слияния» с рекомендациями по разрешению конфликта с помощью маркеров правил слияния .

Ошибки также отображаются в окне журнала событий . Чтобы просмотреть их, выберите «Просмотр» > «Инструменты Windows» > «Журнал событий» .

Чтобы просмотреть полный журнал дерева решений по слиянию, вы можете найти файл журнала в каталоге build/outputs/logs/ вашего модуля с именем manifest-merger- buildVariant -report.txt .

Объединение политик

Инструмент слияния манифестов может логически сопоставлять каждый элемент XML из одного файла манифеста с соответствующим элементом в другом файле. При слиянии каждый элемент сопоставляется с использованием ключа сопоставления , либо уникального значения атрибута (например, android:name ), либо естественной уникальности самого тега (например, может быть только один элемент <supports-screen> ).

Если два манифеста содержат один и тот же элемент XML, инструмент объединяет эти два элемента вместе, используя одну из трех политик слияния:

Объединить
Объедините все неконфликтующие атрибуты в один тег и объедините дочерние элементы в соответствии с соответствующей политикой слияния. Если какие-либо атрибуты конфликтуют друг с другом, объедините их вместе с помощью маркеров правила слияния .
Объединить только детей
Не объединяйте и не объединяйте атрибуты (сохраняйте только атрибуты, предоставленные файлом манифеста с наивысшим приоритетом) и объединяйте дочерние элементы в соответствии с их политикой объединения.
Держать
Оставьте элемент как есть и добавьте его к общему родительскому элементу в объединенном файле. Это используется только тогда, когда допустимо наличие нескольких объявлений одного и того же элемента.

В таблице 3 перечислены все типы элементов, тип используемой политики слияния и ключ, используемый для определения соответствия элемента между двумя манифестами:

Таблица 3. Политики слияния элементов манифеста и ключи сопоставления

Элемент Политика объединения Ключ совпадения
<action> Объединить android:name
<activity> Объединить android:name
<application> Объединить В каждом <manifest> есть только один.
<category> Объединить android:name
<data> Объединить На каждый <intent-filter> есть только один.
<grant-uri-permission> Объединить На каждого <provider> есть только один.
<instrumentation> Объединить android:name
<intent-filter> Держать Нет соответствия; допускается несколько объявлений внутри родительского элемента.
<manifest> Объединить только детей В каждом файле только один.
<meta-data> Объединить android:name
<path-permission> Объединить На каждого <provider> есть только один.
<permission-group> Объединить android:name
<permission> Объединить android:name
<permission-tree> Объединить android:name
<provider> Объединить android:name
<receiver> Объединить android:name
<screen> Объединить android:screenSize
<service> Объединить android:name
<supports-gl-texture> Объединить android:name
<supports-screen> Объединить В каждом <manifest> есть только один.
<uses-configuration> Объединить В каждом <manifest> есть только один.
<uses-feature> Объединить Атрибут android:name (если отсутствует, то атрибут android:glEsVersion )
<uses-library> Объединить android:name
<uses-permission> Объединить android:name
<uses-sdk> Объединить В каждом <manifest> есть только один.
Пользовательские элементы Объединить Нет соответствия; они неизвестны инструменту слияния и всегда включаются в объединенный манифест.

Внедрить переменные сборки в манифест

Если вам нужно вставить в файл AndroidManifest.xml переменные, определенные в файле build.gradle , вы можете сделать это с помощью свойства manifestPlaceholders . Это свойство принимает карту пар ключ-значение, как показано здесь:

классный

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
    }
    ...
}

Котлин

android {
    defaultConfig {
        manifestPlaceholders["hostName"] = "www.example.com"
    }
    ...
}

Затем вы можете вставить один из заполнителей в файл манифеста в качестве значения атрибута:

<intent-filter ... >
    <data android:scheme="https" android:host="${hostName}" ... />
    ...
</intent-filter>

По умолчанию инструменты сборки также предоставляют идентификатор приложения вашего приложения в заполнителе ${applicationId} . Значение всегда соответствует окончательному идентификатору приложения для текущей сборки, включая изменения в вариантах сборки. Это полезно, если вы хотите использовать уникальное пространство имен для идентификаторов, таких как действие намерения, даже между вариантами сборки.

Например, если ваш файл build.gradle выглядит так:

классный

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

Котлин

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

Затем вы можете вставить идентификатор приложения в свой манифест следующим образом:

<intent-filter ... >
    <action android:name="${applicationId}.TRANSMOGRIFY" />
    ...
</intent-filter>

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

<intent-filter ... >
   <action android:name="com.example.myapp.free.TRANSMOGRIFY" />
    ...
</intent-filter>

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