Combinar vários arquivos de manifesto

O arquivo APK ou Android App Bundle pode conter apenas um arquivo AndroidManifest.xml, mas o projeto do Android Studio pode conter vários, disponibilizados pelo conjunto de origem principal ou por variantes de build e bibliotecas importadas. Ao compilar o app, a compilação do Gradle integra todos os arquivos de manifesto em um único arquivo, empacotado em seu app.

A ferramenta de combinação de manifestos combina todos os elementos XML de cada arquivo seguindo algumas heurísticas de integração e obedecendo a preferências definidas com atributos XML especiais. Esta página descreve como funciona a integração de manifestos e como aplicar preferências de combinação para resolver conflitos.

Dica: use a visualização Manifesto integrado para ver os resultados do manifesto integrado e encontrar erros de conflito.

Prioridades de integração

A ferramenta de combinação combina todos os arquivos de manifesto em um único arquivo, integrando-os sequencialmente de acordo com a prioridade de cada um deles. Por exemplo, se você tem três arquivos de manifesto, o de prioridade mais baixa será integrado ao próximo manifesto de maior prioridade. Por sua vez, esse resultado será integrado ao próximo manifesto de maior prioridade, como mostrado na figura 1.

Figura 1. Processo de integração de três arquivos de manifesto, da prioridade mais baixa (esquerda) à mais alta (direita).

Há três tipos básicos de arquivos de manifesto que podem ser integrados entre si, com as seguintes prioridades de intregação (prioridade mais alta primeiro):

  1. Arquivo de manifesto para a variante de build

    Se você tiver vários conjuntos de origem para sua variante, as prioridades dos manifestos serão as seguintes:

    1. Manifesto de variante de compilação (como src/demoDebug/)
    2. Manifesto de tipo de compilação (como src/debug/)
    3. Manifesto de variação de produto (como src/demo/)

      Se você estiver usando dimensões de variações, as prioridades dos manifestos corresponderão à ordem em que cada dimensão é listada na propriedade flavorDimensions (a primeira é a mais alta).

  2. Arquivo de manifesto principal para o módulo de app
  3. Arquivo de manifesto de uma biblioteca incluída

    Se você tiver várias bibliotecas, as prioridades dos manifestos corresponderão à ordem das dependências (a ordem em que aparecem no bloco dependencies do Gradle).

Por exemplo, um manifesto de biblioteca é integrado ao manifesto principal. Em seguida, o manifesto principal é integrado ao manifesto da variante de build.

Importante: as configurações da compilação do arquivo build.gradle modificam todos os atributos correspondentes no arquivo de manifesto integrado. Por exemplo, o minSdkVersion do arquivo build.gradle substitui o atributo correspondente no elemento de manifesto <uses-sdk>. Para evitar confusão, basta não usar o elemento <uses-sdk> e definir essas propriedades apenas no arquivo build.gradle. Para ver mais detalhes, consulte Configurar sua compilação.

Heurística de conflitos de combinação

A ferramenta de combinação pode associar logicamente cada elemento XML de um manifesto a um elemento correspondente em outro manifesto. Para saber mais sobre como essa correspondência funciona, consulte o apêndice sobre políticas de integração.

Se um elemento do manifesto de menor prioridade não corresponder a nenhum elemento no manifesto de maior prioridade, ele será adicionado ao manifesto integrado. No entanto, se houver um elemento correspondente, a ferramenta de combinação tentará combinar todos os atributos de cada elemento no mesmo elemento. Se a ferramenta constatar que os dois manifestos contêm o mesmo atributo com valores diferentes, ocorrerá um conflito de integração.

A tabela 1 mostra os resultados possíveis quando a ferramenta de combinação tenta combinar todos os atributos no mesmo elemento.

Tabela 1. Comportamento padrão de integração para valores de atributo.

Atributo de alta prioridade Atributo de baixa prioridade Resultado combinado do atributo
Nenhum valor Nenhum valor Nenhum valor (usar o valor padrão)
Valor B Valor B
Valor A Nenhum valor Valor A
Valor A Valor A
Valor B Erro de conflito: adicione um marcador de regra de combinação

No entanto, há algumas situações em que a ferramenta de combinação se comporta de maneira diferente para evitar conflitos:

  • Atributos no elemento <manifest> nunca são integrados. Somente os atributos do manifesto de maior prioridade são usados.
  • O atributo android:required nos elementos <uses-feature> e <uses-library> usam uma integração OR. Se houver algum conflito, "true" será aplicado, e a biblioteca ou o recurso exigido por um manifesto sempre será incluído.
  • Atributos no elemento <uses-sdk> sempre usam o valor do manifesto de maior prioridade, exceto nas seguintes situações:
    • Quando o manifesto de menor prioridade tem um valor de minSdkVersion maior, ocorre um erro, a menos que você aplique a regra de combinação overrideLibrary.
    • Quando o manifesto de menor prioridade tem um valor de targetSdkVersion menor, a ferramenta de combinação usa o valor do manifesto de maior prioridade, mas também adiciona as permissões de sistema necessárias para o funcionamento correto da biblioteca importada (para casos em que a versão mais recente do Android tenha restrições de permissão mais rígidas). Para saber mais sobre esse comportamento, consulte a seção sobre permissões de sistema implícitas.
  • O elemento <intent-filter> nunca é correspondido entre manifestos. Cada elemento é tratado como único e adicionado ao elemento pai comum no manifesto integrado.

Para todos os outros conflitos entre atributos, você receberá um erro e precisará instruir a ferramenta de combinação sobre a resolução. Para isso, adicione um atributo especial no arquivo de manifesto de maior prioridade. Consulte a próxima seção sobre marcadores de regra de combinação.

Não confie nos valores padrão dos atributos. Como todos os atributos únicos são combinados no mesmo elemento, isso poderá gerar resultados inesperados se o manifesto de maior prioridade depender realmente do valor padrão de um atributo sem declará-lo. Por exemplo, se o manifesto de maior prioridade não declarar o atributo android:launchMode, ele usará o valor padrão de "standard". No entanto, se o atributo de menor prioridade declarar esse atributo com um valor diferente, esse valor será aplicado ao manifesto integrado (modificando o valor padrão). Portanto, defina explicitamente cada atributo com o valor adequado. Os valores padrão de cada atributo estão documentados na Referência de manifestos.

Marcadores de regra de combinação

Um marcador de regra de combinação é um atributo XML que pode ser usado para indicar sua preferência sobre como resolver conflitos de integração ou remover elementos e atributos inadequados. O marcador pode ser aplicado a todo um elemento ou apenas a atributos específicos de um elemento.

Na integração de dois arquivos de manifesto, a ferramenta de combinação procura esses marcadores nos arquivos de manifesto de maior prioridade.

Todos os marcadores pertencem ao namespace tools do Android. Portanto, é necessário declarar primeiro esse namespace no elemento <manifest> da seguinte forma:

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

Marcadores de nó

Para aplicar uma regra de combinação a todo um elemento XML (a todos os atributos de determinado elemento de manifesto e a todas as tags filhas correspondentes), use os seguintes atributos:

tools:node="merge"
Integrar todos os atributos nessa tag e todos os elementos aninhados quando não há conflitos usando a heurística de conflitos de integração. Esse é o comportamento padrão para elementos.

Manifesto de baixa prioridade:

<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>

Manifesto de alta prioridade:

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

Resultado do manifesto integrado:

<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"
Integrar atributos somente nessa tag. Não integrar elementos aninhados.

Manifesto de baixa prioridade:

<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>

Manifesto de alta prioridade:

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

Resultado do manifesto integrado:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateUnchanged">
</activity>
tools:node="remove"
Remover esse elemento do manifesto integrado. Embora possa parecer que você precise simplesmente excluir esse elemento, é preciso usar esse atributo quando você descobre um elemento desnecessário no manifesto integrado, disponibilizado por um arquivo de manifesto de menor prioridade que está fora do seu controle (como uma biblioteca importada).

Manifesto de baixa prioridade:

<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>

Manifesto de alta prioridade:

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

Resultado do manifesto integrado:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="duck"
      android:value="@string/quack"/>
</activity-alias>
tools:node="removeAll"
Semelhante a tools:node="remove", mas remove todos os elementos que correspondem a esse tipo de elemento (dentro do mesmo elemento pai).

Manifesto de baixa prioridade:

<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>

Manifesto de alta prioridade:

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

Resultado do manifesto integrado:

<activity-alias android:name="com.example.alias">
</activity-alias>
tools:node="replace"
Substituir completamente o elemento de menor prioridade. Ou seja, se existir um elemento correspondente no manifesto de menor prioridade, ele será ignorado e o elemento será usado exatamente como aparece neste manifesto.

Manifesto de baixa prioridade:

<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>

Manifesto de alta prioridade:

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

Resultado do manifesto integrado:

<activity-alias android:name="com.example.alias">
  <meta-data android:name="fox"
      android:value="@string/dingeringeding"/>
</activity-alias>
tools:node="strict"
Gerar um erro de compilação todas as vezes que este elemento no manifesto de menor prioridade não corresponder exatamente ao elemento no manifesto de maior prioridade (a menos que resolvido por outros marcadores de regra de combinação). Isso modifica a heurística de conflitos de integração. Por exemplo, se o manifesto de menor prioridade incluir um atributo extra, a compilação vai falhar (enquanto o comportamento padrão adiciona o atributo extra ao manifesto integrado).

Manifesto de baixa prioridade:

<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>

Manifesto de alta prioridade:

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

Isso cria um erro de combinação de manifesto. No modo rígido, os dois elementos de manifesto não podem ter qualquer diferença. Portanto, é necessário aplicar outros marcadores de regra de combinação para resolver essas diferenças. Normalmente, os dois elementos serão integrados sem problemas, como mostrado nos exemplos de tools:node="merge" acima.

Marcadores de atributo

Para aplicar uma regra de combinação apenas a atributos específicos de uma tag de manifesto, use os atributos a seguir. Cada atributo aceita um ou mais nomes (incluindo o namespace do atributo), separados por vírgulas.

tools:remove="attr, ..."
Remover os atributos especificados do manifesto integrado. Embora pareça ser possível simplesmente excluir esses atributos, é necessário usar esse atributo quando o arquivo de manifesto de menor prioridade inclui esses atributos e você quer garantir que eles não entrem no manifesto integrado.

Manifesto de baixa prioridade:

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

Manifesto de alta prioridade:

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

Resultado do manifesto integrado:

<activity android:name="com.example.ActivityOne"
    android:screenOrientation="portrait">
tools:replace="attr, ..."
Substituir os atributos especificados no manifesto de menor prioridade por aqueles deste manifesto. Em outras palavras, manter sempre os valores do manifesto de maior prioridade.

Manifesto de baixa prioridade:

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

Manifesto de alta prioridade:

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

Resultado do manifesto integrado:

<activity android:name="com.example.ActivityOne"
    android:theme="@newtheme"
    android:exported="true"
    android:screenOrientation="portrait"
    android:windowSoftInputMode="stateUnchanged">
tools:strict="attr, ..."
Gerar um erro de compilação sempre que esses atributos no manifesto de menor prioridade não corresponderem exatamente aos do manifesto de maior prioridade. Esse é o comportamento padrão para todos os atributos, exceto os que têm comportamentos especiais, conforme descrito na heurística de conflitos de integração.

Manifesto de baixa prioridade:

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

Manifesto de alta prioridade:

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

Isso cria um erro de combinação de manifesto. Aplique outros marcadores de regra de combinação para resolver o conflito. Lembre-se: esse é o comportamento padrão. Portanto, o exemplo acima terá o mesmo resultado se você remover tools:strict="screenOrientation".

Também é possível aplicar vários marcadores a um elemento da seguinte maneira:

Manifesto de baixa prioridade:

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

Manifesto de alta prioridade:

<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">

Resultado do manifesto integrado:

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

Seletor de marcadores

Se você quiser aplicar os marcadores de regra de combinação apenas a uma biblioteca importada específica, adicione o atributo tools:selector com o nome do pacote da biblioteca.

Por exemplo, com o manifesto a seguir, a regra de combinação remove será aplicada apenas quando o arquivo de manifesto de menor prioridade for da biblioteca com.example.lib1.

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

Se o manifesto de menor prioridade tiver qualquer outra origem, a regra de combinação remove será ignorada.

Observação: se você usar esse atributo com um dos marcadores de atributo, ele será aplicado a todos os atributos especificados no marcador.

Modificar <uses-sdk> para bibliotecas importadas

Por padrão, na importação de uma biblioteca com um valor de minSdkVersion maior que o arquivo de manifesto principal, ocorre um erro e não é possível importar a biblioteca. Para fazer com que a ferramenta de combinação ignore esse conflito e importe a biblioteca, mantendo o valor menor de minSdkVersion do app, adicione o atributo overrideLibrary à tag <uses-sdk>. O valor do atributo pode ser um ou mais nomes de pacotes de biblioteca (separados por vírgulas), indicando as bibliotecas que podem modificar a minSdkVersion do manifesto principal.

Por exemplo, se o manifesto principal do app aplicar overrideLibrary desta forma:

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

O manifesto a seguir poderá ser integrado sem gerar erro relacionado à tag <uses-sdk>, e o manifesto integrado manterá o minSdkVersion="2" do manifesto do app.

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

Permissões de sistema implícitas

Algumas APIs do Android que antes eram acessadas livremente por apps passaram a ser restringidas por permissões de sistema em versões recentes do Android. Para evitar o cancelamento de apps que contam com o acesso a essas APIs, as versões recentes do Android permitem que os apps continuem acessando essas APIs sem a permissão, desde que definam targetSdkVersion com um valor inferior à versão em que a restrição foi implementada. Na prática, esse comportamento concede ao app uma permissão implícita para acessar as APIs. Portanto, isso pode afetar manifestos integrados com valores diferentes de targetSdkVersion da seguinte forma.

Se o arquivo de manifesto de menor prioridade tiver um valor menor de targetSdkVersion que forneça uma permissão implícita e o manifesto de maior prioridade não tiver a mesma permissão implícita (porque targetSdkVersion é maior ou igual à versão em que a restrição foi implementada), a ferramenta de combinação adicionará explicitamente a permissão de sistema ao manifesto integrado.

Por exemplo, se o app definir targetSdkVersion como 4 ou maior e importar uma biblioteca com targetSdkVersion definido como 3 ou menor, a ferramenta de combinação adicionará a permissão WRITE_EXTERNAL_STORAGE ao manifesto integrado. A Tabela 2 lista todas as permissões possíveis que podem ser adicionadas ao manifesto integrado.

Tabela 2. Lista de permissões que a ferramenta de combinação pode adicionar ao manifesto integrado.

Declaração do manifesto de menor prioridade Permissões adicionadas ao manifesto combinado
targetSdkVersion é 3 ou menor WRITE_EXTERNAL_STORAGE, READ_PHONE_STATE
targetSdkVersion é 15 ou menor e usa READ_CONTACTS READ_CALL_LOG
targetSdkVersion é 15 ou menor e usa WRITE_CONTACTS WRITE_CALL_LOG

Inspecionar o manifesto combinado e encontrar conflitos

Você pode ver o manifesto integrado mesmo antes de criar o app abrindo o arquivo AndroidManifest.xml no Android Studio e clicando na guia Manifesto integrado na parte inferior do editor.

A visualização "Manifesto integrado" mostra os resultados do manifesto integrado à esquerda e as informações sobre cada arquivo dele à direita, conforme mostrado na Figura 2. Os elementos integrados dos arquivos de manifesto de menor prioridade são destacados em cores diferentes à esquerda. O significado de cada cor é especificado em Origens do manifesto, à direita.

Figura 2. Visualização "Manifesto integrado"

Arquivos de manifesto que eram parte do build, mas não contribuíram com elementos ou atributos, são listados em Outro arquivos do manifesto à direita.

Para ver informações sobre a origem de um elemento, clique nele à esquerda. Os detalhes serão exibidos em Merging Log, à direita.

Se ocorrer um conflito, ele será exibido em Merging Errors no lado direito, com uma recomendação sobre como resolver o conflito usando marcadores de regra de combinação. Os erros também são impressos na janela Event Log (selecione View > Tool Windows > Event Log).

Para ver um registro completo da árvore de decisões da integração, o arquivo de registros está no diretório build/outputs/logs/ do módulo e é denominado manifest-merger-buildVariant-report.txt.

Apêndice: políticas de integração

A ferramenta de combinação de manifestos pode associar logicamente cada elemento XML de um arquivo de manifesto a um elemento correspondente em outro arquivo. A ferramenta de combinação corresponde cada elemento usando uma "chave de correspondência": um valor de atributo único (como android:name) ou a unicidade natural da própria tag (por exemplo, pode existir somente um elemento <supports-screen>). Se dois manifestos tiverem o mesmo elemento XML, a ferramenta integrará os dois elementos usando uma das três políticas de integração a seguir:

Mesclar
Combinar todos os atributos não conflitantes na mesma tag e integrar elementos filhos de acordo com a respectiva política de integração. Se houver um conflito entre atributos, integrá-los com os marcadores de regra de combinação.
Integrar apenas filhos
Não combinar ou integrar atributos (manter apenas os atributos disponibilizados pelo arquivo de manifesto de maior prioridade) e integrar elementos filhos de acordo com a política de integração deles.
Manter
Deixar o elemento "como está" e adicioná-lo ao elemento pai comum no arquivo integrado. Usado apenas quando a existência de várias declarações para o mesmo elemento é aceitável.

A tabela 3 lista cada tipo de elemento, o tipo de política de integração usada e a chave usada para determinar uma correspondência de elemento entre dois manifestos.

Tabela 3. Políticas e chaves de correspondência de integração de elementos de manifesto

Elemento Política de combinação Chave de correspondência
<action> Integrar Atributo android:name
<activity> Integrar Atributo android:name
<application> Integrar Há apenas um por <manifest>
<category> Integrar Atributo android:name
<data> Integrar Há apenas um por <intent-filter>
<grant-uri-permission> Integrar Há apenas um por <provider>
<instrumentation> Integrar Atributo android:name
<intent-filter> Manter Sem correspondência; são permitidas várias declarações dentro do mesmo elemento pai
<manifest> Combinar apenas filhos Há apenas um por arquivo
<meta-data> Integrar Atributo android:name
<path-permission> Integrar Há apenas um por <provider>
<permission-group> Integrar Atributo android:name
<permission> Integrar Atributo android:name
<permission-tree> Integrar Atributo android:name
<provider> Integrar Atributo android:name
<receiver> Integrar Atributo android:name
<screen> Integrar Atributo android:screenSize
<service> Integrar Atributo android:name
<supports-gl-texture> Integrar Atributo android:name
<supports-screen> Integrar Há apenas um por <manifest>
<uses-configuration> Integrar Há apenas um por <manifest>
<uses-feature> Integrar Atributo android:name (se ausente, o atributo android:glEsVersion)
<uses-library> Integrar Atributo android:name
<uses-permission> Integrar Atributo android:name
<uses-sdk> Integrar Há apenas um por <manifest>
Elementos personalizados Integrar Sem correspondência; são desconhecidos para a ferramenta de combinação e, portanto, são sempre incluídos no manifesto integrado