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

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

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

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

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

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

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

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

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

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

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

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

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

      Если вы используете измерения flavor, приоритеты манифеста соответствуют порядку, в котором каждое измерение указано в свойстве 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="mergeOnlyAttributes"
Объединяйте атрибуты только в этом теге; не объединяйте вложенные элементы.

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

<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="mergeOnlyAttributes">
</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.

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

Рисунок 2. Вид объединенного манифеста.

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

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

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

Ошибки также печатаются в окне журнала событий . Чтобы просмотреть их, выберите Вид > Окна инструментов > Журнал событий .

Чтобы просмотреть полный журнал дерева решений по слиянию, вы можете найти файл журнала в каталоге 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>

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