Fusão de Manifestos: Como são aplicadas regras para gerar um único arquivo AndroidManifest.xml

Haldny Santos
Android Dev BR
Published in
6 min readMar 22, 2019

--

Foto por lee atwood em Unsplash

Quando criamos o nosso projeto Android, utilizando o Android Studio, um arquivo AndroidManifest.xml é gerado, geralmente no diretório app/src/main onde podemos declarar os nossos componentes e outras informações para a execução do nosso APK no sistema operacional Android. Entretanto, o projeto pode conter vários outros arquivos AndroidManifest.xml levando em consideraçaão o tipo de build, flavor e bibliotecas importadas. Então, ao criar o seu aplicativo o gradle mescla todos os arquivos de manifesto em um único, que é empacotado no seu APK.

Prioridades na mesclagem

A ferramenta de fusão combina todos os arquivos de manifesto em um arquivo mesclando-os sequencialmente com base na prioridade de cada arquivo de manifesto. Por exemplo, se existerem três arquivos de manifesto, um manifesto de prioridade mais baixa, um manifesto de prioridade média e um manifesto de prioridade mais alta. O manifesto de prioridade mais baixa será mesclado no de prioridade média e, em seguida, será mesclado no manifesto de prioridade mais alta.

  • Prioridade mais alta: Configuração de manifesto portipo de build
  • Prioridade alta: Configuração de manifesto porflavors
  • Prioridade Média: Manifesto no diretóriosrc/main/ da aplicação
  • Prioridade Baixa: Manifesto de dependencias e bibliotecas

Conflitos na fusão

Conflitos na fusão ocorrem quando os manifestos mesclados contêm o mesmo elemento, mas com o valor de atributo diferente e que não é resolvido com base nas regras de conflito de mesclagem padrão.

Regras de mesclagem padrão

Os marcadores e seletores de conflitos também podem definir regras de mesclagem personalizadas, como permitir que uma biblioteca importada tenha uma minSdkVersion superior à versão definida nos outros manifestos de prioridade mais alta.

No entanto, existem algumas situações em que a ferramenta de fusão se comporta de forma diferente para evitar conflitos de fusão:

  • Atributos no elemento<manifest> nunca são mesclados, apenas os atributos do manifesto de prioridade mais alta são usados.
  • O atributoandroid:required nos elementos<uses-feature> e <uses-library> usam uma mesclagem OR, de modo que, se houver conflito, "true"será aplicado e o recurso ou biblioteca requerido por um manifesto será sempre incluído.
  • Atributos no elemento<uses-sdk> sempre usam o valor do manifesto de prioridade mais alta, exceto nas seguintes situações:
  • Quando o manifesto de prioridade mais baixa tem o valor do minSdkVersion maior, um erro ocorre, a menos que você aplique a regra de mesclagem overrideLibrary.
  • Quando o manifesto de prioridade mais baixa tem um valor targetSdkVersion menor, a ferramenta de fusão usa o valor do manifesto de prioridade mais alta, e também adiciona quaisquer permissões do sistema necessárias para garantir que a biblioteca importada continue a funcionar corretamente (para casos em que a versão do Android tenha aumentado as restrições de permissão).
  • O elemento <intent-filter> não coincidem entre os manifestos. Cada um é tratado como exclusivo e é adicionado ao elemento pai comum no manifesto mesclado.

Marcadores de mesclagem

Um marcador de regra de mesclagem é um atributo XML que você pode usar para expressar sua preferência sobre como resolver conflitos de mesclagem ou remover elementos e atributos indesejados. Você pode aplicar um marcador a um elemento inteiro ou a apenas atributos específicos em um elemento.

Ao mesclar dois arquivos de manifesto, a ferramenta de fusão procura esses marcadores no arquivo de manifesto de prioridade mais alta.

Todos os marcadores pertencem ao namespace de ferramentas do Android, portanto, primeiro você deve declarar esse namespace no elemento <manifest>, conforme mostrado abaixo:

<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 mesclagem a um elemento do XML inteiro (a todos os atributos em um determinado elemento de manifesto e a todas as suas tags filhas), use os seguintes atributos: merge, replace, strict, remove e removeAll.

tools:node=”merge”

Mescla todos os atributos e todos os elementos aninhados quando não houver conflitos usando a heurística de conflito de mesclagem padrão. Esse é o comportamento padrão dos 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 mesclado:

<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=”replace”

Substitui os atributos nos manifestos de prioridade mais baixa com as informações do manifesto com prioridade mais alta.

Manifesto de baixa prioridade:

<activity android:name="com.example.ActivityOne"
android:windowSoftInputMode="stateUnchanged"
android:screenOrientation="landscape">
<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="replace">
</activity>

Resultado do manifesto mesclado:

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

tools:node=”strict”

Define uma política de fusão para que os elementos mesclados com os mesmos atributos, mas com valores diferentes gerem uma falha de compilação, a menos que sejam resolvidos por meio das regras de conflito.

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:node="strict">
</activity>

Resultado do manifesto mesclado:

Manifest merger failed : Node application at AndroidManifest.xml:23:5-620:19 is tagged with tools:node="strict", yet application at AndroidManifest.xml:10:5-16:19 is different : Attribute application@screenOrientation do not match: portrait versus landscape at AndroidManifest.xml:10:5-16:19

tools:node=”remove”

Remove o elemento de prioridade inferior especificado do manifesto mesclado.

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>
<activity android:name="com.example.ActivityTwo"
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="remove">
</activity>

Resultado do manifesto mesclado:

<activity android:name="com.example.ActivityTwo"
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=”removeAll”

Remove todos os elementos de prioridade inferior do mesmo tipo de nó do manifesto mesclado, esse marcador não pode ser usado em elementos que possuam o atributo android:name.

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>
<activity android:name="com.example.ActivityTwo"
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:windowSoftInputMode="stateUnchanged">
<intent-filter tools:node="removeAll">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

Resultado do manifesto mesclado:

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

Marcadores de atributo

Podemos também aplicar regras de fusão apenas para atributos específicos em uma tag do manifesto. Cada regra aceita um ou mais nomes de atributos (incluindo o namespace do atributo), separados por vírgulas.

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

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

Marcadores de seletor

Se você quiser aplicar uma regra de fusão somente a uma biblioteca importada específica, inclua o atributo tools:selector com o nome do pacote da biblioteca.

Por exemplo, com o seguinte manifesto, a regra de mesclagem de remoção é aplicada somente quando o arquivo de manifesto de baixa prioridade é da bibliotecacom.haldny.biblioteca1.

<permission android:name="permissionOne"
tools:node="remove"
tools:selector="com.haldny.biblioteca1">

Se o manifesto de prioridade mais baixa for de qualquer outra origem, que não seja com.haldny.biblioteca1, a regra de mesclagem de remoção será ignorada.

Substituir <uses-sdk> para bibliotecas importadas

Por padrão, ao importar uma biblioteca com um valor minSdkVersion maior que no arquivo de manifesto principal, um erro ocorre e a biblioteca nao pode ser importada. Para fazer com que a ferramenta de fusão ignore esse conflito e importe a biblioteca, mantendo o valor minSdkVersion inferior ao do aplicativo, adicione o atributooverrideLibrary na tag <uses-sdk>. O valor do atributo pode ter um ou mais nomes de pacotes de bibliotecas (separados por vírgula), indicando as bibliotecas que podem substituir o minSdkVersion do manifesto principal.

Por exemplo, se o manifesto principal do seu aplicativo aplicar overrideLibrary da seguinte maneira:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.haldny.myapp"
xmlns:tools="http://schemas.android.com/tools">
<uses-sdk android:targetSdkVersion="22" android:minSdkVersion="2"
tools:overrideLibrary="com.haldny.biblioteca1, com.haldny.biblioteca2"/>

~Se você gostou deste artigo, clique no 👏 abaixo para que mais pessoas possam vê-lo. Também você pode me seguir no Medium, e ai você pode receber atualizações sobre o meu próximo artigo!

--

--

Haldny Santos
Android Dev BR

Dad(Enzo) & Husband(@rafaela.araujo.silva) | Software Engineer at @inovacao_cesar | Android | Soccer Lover | Creative