【Android Studio】Manifest Merger

Manifest Merger


This document describes the new manifest merger tool.

This new merger was introduced in version 0.10 of the plugin. As of 0.11, this tool is used by default by the gradle plugin.

In order to revert to the old manifest merger, please add to your build.gradle the following configuration :

android {
useOldManifestMerger true
}

Manifest files ordering


In general, there are three types of manifest files that need to be merged into a single resulting app manifest, here in priority order :

  1. Product flavors and build types specific manifest files.

  2. Main manifest file for the application.

  3. Library manifest files.


Manifest files of types (1) are usually overriding the main manifest content since it’s specializing the application for a particular delivery. Then, manifest files of types (3) are usually merged into the resulting main manifest from previous step. The rules for merging depend on each node type and can be altered using “tools:” namespace attributes.


Because of multiple product flavors and build types there can be a matrix of possible manifest files to merge. However, for each assembly step, only one value of each flavor group and build type can be selected, leading to an ordered list of possible manifest files that overrides the main manifest file.


For instance, the following FlavorGroups: abi, density, API, Prod/Internal leads to the following matrix of possible flavors :



ABI

Density

API

Prod/Internal

x86

mdpi

9

prod

arm

high

14

internal

mips

xhigh

15

 
 

xxhigh

  

This leads to 3x4x3x2 possible combinations. However, for each assembly run, there can only be one flavor from each group, in the order the flavor group were declared in the original build.gradle leading to an ordered list of higher priority to lower priority potential manifest files to merge.


For instance, building the x86-high-15-prod variant will lookup the following manifest files to merge

  1. x86/AndroidManifest.xml

  2. high/AndroidManifest.xml

  3. 15/AndroidManifest.xml

  4. internal/AndroidManifest.xml


Each file in this ordered list has a priority according to its rank in the list, and merging will use this priority to decide which XML element or attribute will overwrite a lower priority setting.


Therefore, the input of the merging tool will be as follows :

  • ordered list by priority of flavor/build type manifest files, these will be commonly referenced as the flavors manifests.

  • main manifest file

  • ordered list by declaration (or transitive dependency) of libraries manifest files.

  • Injectable values for placeholders and XML generation

Android Manifest file merging


Each element in a manifest file can be identified by its element type (e.g. activity, intent-filter) and an optional key value. Some elements like “activity” must have a key since there can be multiple present in a single AndroidManifest.xml. Other elements like “application” do not require a key since there can be only one.


The element type and key value pair represent the identity of a manifest element.


The merging activity is always between two identically typed elements, one coming from a higher priority manifest file, one coming from a lower priority manifest file. Each merging activity has a default behavior that will be described subsequently. Also each default merging activity on the node or on a specific attribute can potentially be overridden by including tools specific markers.


The merging process will also record all decisions taken along the way for each node, described in the "Logging" section.

Elements and Attributes merging process

Implicit declarations

A number of attributes can have default values (defined in the online docs as defaults). When a higher priority element does not define an attribute which has a default value of X, if a lower priority element defines that very attribute with the same value X, it will still be added to the merged element (with value X of course) since it represents a clear choice from the library to not consider this value as the default value but the right value for the feature (in case the default ever changes).


Most attributes with default values will be ignored by the manifest merger if a lower priority attribute defines a value; it will be merged since there is no conflict between a default value and an attribute set to any value. The attribute will be set to that set value in the resulting merged element.


However, there are a few exceptions listed below:


<uses-feature android:required>

Defaults to true. Merging with other attributes will use a OR merging policy since if any library requires the feature, the resulting application will require the feature.

<uses-library android:required>

Same as uses-feature:required.

<uses-sdk android:minSdkVersion>

Defaults to 1.

The higher priority document's version will be used but importing a library with a more recent version will generate an error.        

<uses-sdk android:maxSdkVersion>

Same as uses-sdk:minSdkVersion

<uses-sdk android:targetSdkVersion>

Same as uses-sdk:minSdkVersion


Automatic upgrades

When importing a library with a lower target SDK than the project’s, it may be necessary to explicitly grant permissions (and perhaps make other changes) for the library to function properly in the later runtime. This will be performed automatically by the manifest merger.

Placeholder support

When an attribute value contains a placeholder (see format below), the manifest merger will swap this placeholder value with an injected value. Injected values are specified in the build.gradle.

The syntax for placeholder values is ${name} since @ is reserved for links. After the last file merging occurred, and before the resulting merged android manifest file is written out, all values with a placeholder will be swapped with injected values. A build breakage will be generated if a variable name is unknown.


The placeholder string can have a prefix and/or a suffix allowing to partially replace the value.


Examples:


android:authority="${applicationId}.foo"

android:authority=”com.acme.${localApplicationId}”

android:authority=”com.acme.${localApplicationId}.foo”


An implicit placeholder ${applicationId} resolution will be automatically provided out of the box with the build.gradle applicationId value.


Examples :


<activity

android:name=".Main">

     <intent-filter>

     <action android:name="${applicationId}.foo">

         </action>

</intent-filter>

</activity>


with the following gradle declaration :


android {

   compileSdkVersion 19

   buildToolsVersion "19.0.2"


   productFlavors {

       flavor1 {

           applicationId = "com.android.tests.flavorlib.app.flavor1"

       }

}


Once merged, the <action android:name> will be

<action android:name=“com.android.tests.flavorlib.app.flavor1.foo”>


For custom placeholders replacements, use the following DSL to configure the placeholders values :


android { defaultConfig { manifestPlaceholders = [ activityLabel:"defaultName"] } productFlavors { free { } pro { manifestPlaceholders = [ activityLabel:"proName" ] } }


will substitute the placeholder in the following declaration :
<activity android:name=".MainActivity" android:label="${activityLabel}" >

Generic description of merging strategies


Merging XML can happen either at the node level or at the attribute level.


At the node level, the default merging policy is to merge attributes and sub-elements as long as there is no conflict. A conflict arises when two elements with the same identity have the same attribute with a different value.


For instance:

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@theme1”/>


merging with the following declaration will not generate a conflict:

<activity

  android:name=”com.foo.bar.ActivityOne”

android:screenOrientation=”landscape/>


same for

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@theme1”/>


merging with the following declaration will not generate a conflict, since the “theme” attribute defined in both elements have the same value.

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@theme1”

android:screenOrientation=”landscape/>


however,

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@theme1”/>


merging with the following declaration will generate a conflict, since the “theme” attribute defined in both elements does not have the same value.

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@theme2”

android:screenOrientation=”landscape/>


Now, each element can have sub-elements and rules for matching those will follow the same general principle, sub-elements with the same identity will be matched together. If a sub-element exist in only one of the two parents, this is not a conflict.


Markers


A marker is a special attribute, in the tools namespace, used to express a specific decision for how to resolve conflicts.


All markers belong to the Android tools namespace, therefore you must declare the namespace in any AndroidManifest.xml containing at least one marker :

xmlns:tools="http://schemas.android.com/tools"


Example:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.android.tests.flavorlib.app"

   xmlns:tools="http://schemas.android.com/tools">


   <application

       android:icon="@drawable/icon"

       android:label="@string/app_name"

       tools:replace=”icon, label”/>

</manifest>


When conflicts arise between elements to be merged, some markers must be explicitly added to guide the Manifest Merger.  At the node level, the tools:node attribute should be used, at the attribute level, the tools:attr attribute should be used.


tools:node markers


A tools:node=”maker_value” attribute should be present on the node which is in conflict and need resolution.  


<tools:node> attribute value

manifest merger action

<tools:node=”merge”>

This is the implicit default mode for node merging, nodes will be merged as long as they do not conflict

<tools:node=”replace”>

Replace the lower priority declaration with the annotated one.

<tools:node=”strict”>

This will generate a build failure when another node with the same identity is present and is not strictly equal.

<tools:node=”merge-only-attributes”>

Only merge attributes from the lower priority declaration.

<tools:node=”remove”>

Remove the annotated element from the resulting XML. No lower priority declaration will be merged in irrespective of potential conflicts.

<tools:node=”removeAll”>

Remove all elements of the same node type (no key necessary).


tools:attr markers


There can be be many attribute related markers on any particular element to resolve all attributes in conflict.


<tools:strict=”x, y, z”>

Default implicit mode for attribute, generate an error when trying the merge lower priority attribute declaration with a different value.

<tools:remove=”x, y, z”>

Remove the x, y, z attributes from any lower priority declaration when merging in.

<tools:replace=”x, y, z”>

Replace the x, y, z attributes from any lower priority declaration with the provided value (must be present on the same node).


Selector


Each tools:node or tools:attr declaration can be augmented by a tools:selector attribute which is contextual information on whether or not the merging strategy should be applied to the current lower priority XML description. For instance, this is useful when removing a permission only if coming for one particular library as opposed to any library:


   <permission

         android:name="permissionOne"

         tools:node="remove"

         tools:selector="com.example.lib1">

tools:overrideLibrary marker

A special marker that can only be used with uses-sdk declaration to override importing a library which minimum SDK version is more recent than that application's minimum SDK version.
Without such a marker, the manifest merger will fail. The marker will allow users to select which libraries can be imported ignoring the minimum SDK version. 

Example, In the main android manifest : 
<uses-sdk android:targetSdkVersion="14" android:minSdkVersion="2"

tools:overrideLibrary="com.example.lib1, com.example.lib2"/>


will allow the library with the following manifest to be imported without error : 
<manifest xmlns:android="http://schemas.android.com/apk/res/android"

        package="com.example.lib1">        <uses-sdk android:minSdkVersion="4" />    </manifest>

Logging


Each operation/decision made during the manifest merger needs to be

  • recorded

  • formatted for machine capable parsing

  • ordered by node (since several decisions might have contributed to a node’s attributes)


The logging will not be organized as a linear set of events and decisions that ultimately yielded the output file. Instead, to make things easier for developers, the log file will be organized by top-level XML nodes encountered in the input files (whether or not it is present in the output file as we want to document node deletion as well).

Logging record:


A logging record is either a node record (describing all actions taken on that particular node) or a message record containing error messages and warnings.


logging-file = (logging-record)*

logging-record = node-record | message

Message

message=file:line_number:colum_number severity:\ndescription

description=(\tTEXT\n)*


Name

Value

file

Input file generating the log entry

line-number

Input file line number generating the log entry

column-number

Input file column number generating the log entry.

severity

Error, Warning, Info

description

Log entry payload


Examples


/Users/jedo/src/app/src/main/AndroidManifest.xml:3:9 Error:

Attribute activity@screenOrientation value=(portrait) from AndroidManifest.xml:3:9

is also present at flavorlib:lib1:unspecified:3:18 value=(landscape)

Suggestion: add 'tools:replace="icon"' to <activity> element at AndroidManifest.xml:1:5 to override

NodeRecord

Name

Value

node-type

XML node type

node-key

node key attribute value

record-type

[ Action | Log ] *


node-type#node-key\n

\t(node_action:Action)*

\t\t(attribute_action:Action)*

Action format

Name

Value

action-type

added | rejected | implied

target

node | attribute

target-name

node key name or attribute name

origin

origin value location.

Examples:

application

ADDED from AndroidManifest.xml:10:5

MERGED from flavorlib:lib2:unspecified:3:5

android:label

ADDED from AndroidManifest.xml:12:9

REJECTED from flavorlib:lib2:unspecified:3:55

android:icon

ADDED from AndroidManifest.xml:11:9

REJECTED from flavorlib:lib2:unspecified:3:18



receiver#com.example.WidgetReceiver

ADDED from ManifestMerger2Test0_main.xml:60:9

android:label ADDED from ManifestMerger2Test0_main.xml:61:13

android:icon ADDED from ManifestMerger2Test0_main.xml:62:13

android:name ADDED from ManifestMerger2Test0_main.xml:63:13

Build error


When a build error happens, the log for the particular node failing should be displayed followed by a descriptive error message for the user. For instance :


Higher priority declaration

<activity

  android:name=”com.foo.bar.ActivityOne”

android:screenOrientation=”portrait”

android:theme=”@theme1”/>


with a lower priority declaration :

<activity

  android:name=”com.foo.bar.ActivityOne”

android:screenOrientation=”landscape/>


will result in both the log file and the output (exact format for human and machine and IDE readable to be determined).


/Users/jedo/src/app/src/main/AndroidManifest.xml:3:9 Error:

Attribute activity@screenOrientation value=(portrait) from AndroidManifest.xml:3:9

is also present at flavorlib:lib1:unspecified:3:18 value=(landscape)

Suggestion: add 'tools:replace="icon"' to <activity> element at AndroidManifest.xml:1:5 to override


Blame

A “blame” type of output can be obtained to qualify each element and attribute resulting in the merged XML with some indication of where the element/attribute originated from.

Merging Policies


Each element type has a specific default merging policy attached to it. For example most elements types like activity or application have a default merging policy where all attributes and children are merged (assuming no conflict) into the resulting element. However other elements like the top level manifest will have a default merging policy of merging children only which mean that no attributes of the manifest element of a lower priority AndroidManifest.xml is eligible for merging.


Each element can also have a key associated or not. For instance, application have no key, there can only be one <application> elements per AndroidManifest.xml. Most keyed elements use the android:name attribute to express their key value, others

Merge

Non conflicting attributes are merged and children are merged according to their respective merging policy.

Merge Children Only

Attributes are not merged, only children are merged according to their respective merging policy.

Always

Elements are always kept “as is” and added to the common parent in the resulting merged file.


List of element merging policy and key


Node Type

Merging Policy

Key

action

merge

android:name attribute

activity

merge

android:name attribute

application

merge

no key

category

merge

android:name attribute

data

merge

no key

grant-uri-permission

merge

no key

instrumentation

merge

android:name attribute

intent-filter

always

sub-element actions and categories android:name attribute. Several declaration with the same key are allowed.

manifest

merge children only

no key

meta-data

merge

android:name attribute

path-permission

merge

no key

permission-group

merge

android:name attribute

permission

merge

android:name attribute

permission-tree

merge

android:name attribute

provider

merge

android:name attribute

receiver

merge

android:name attribute

screen

merge

attribute screenSize

service

merge

android:name attribute

supports-gl-texture

merge

android:name attribute

supports-screen

merge

no key

uses-configuration

merge

no key

uses-feature

merge

attribute name first and if not present attribute glEsVersion

uses-library

merge

android:name attribute

uses-permission

merge

android:name attribute

uses-sdk

merge

no key

custom elements

merge

no key


Package name smart substitution

Some attributes are package dependent attributes which means the attribute supports smart substitution of partially fully qualified class names with package settings as provided by the manifest node's package attribute.


Each of the attribute described below can have a partial class name that is either starting with a dot or do not contain any dots.


Examples :


<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.app1">


   <application>

       <activity android:name=".Main" />

   </application>

</manifest>


will be expanded into :


   <application>

       <activity android:name="com.example.app1.Main" />

   </application>


This is independent of any package setting in the build.gradle for if for example, you build.gradle contains :


android {

   compileSdkVersion 19

   buildToolsVersion "19.0.2"


   productFlavors {

       flavor1 {

           applicationId = "com.android.tests.flavorlib.app.flavor1"

       }

}


the resulting expansion will still be

<activity android:name=”com.example.app1.Main”>


If you need the injected value as the expanded attribute value use ${applicationId} placeholder, for example :


<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.app1">


   <application>

       <activity android:name="${applicationId}.Main" />

   </application>

</manifest>


List of package dependent attributes that can use this smart substitution facility :

Node Type

Attribute local names

activity

name, parentActivityName

activity-alias

name, targetActivity

application

name, backupAgent

instrumentation

name

provider

name

receiver

name

service

name


Attributes markers examples

Override an attribute coming from a library


Using tools:replace=”x, y, z” will override x,y,z attributes from the imported library’s activity XML declarations.


Higher Priority declaration

<activity

  android:name=”com.foo.bar.ActivityOne”

android:screenOrientation=”portrait”

android:theme=”@theme1”

tools:replace=”theme”/>


with a lower priority declaration :

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@olddogtheme”

android:windowSoftInputMode=”stateUnchanged”

android:exported=”true”>


will result in :

<activity

  android:name=”com.foo.bar.ActivityOne”

android:screenOrientation=”portrait”

android:theme=”@theme1”

android:windowSoftInputMode=”stateUnchanged”

android:exported=”true”/>

Remove an attribute coming from the library.


Using tools:remove=”x, y, z” will remove the x, y, z attributes declaration from the resulting XML.


Higher priority declaration

<activity

  android:name=”com.foo.bar.ActivityOne”

android:hardwareAccelerated=”true”

tools:remove=”android:theme,android:screenOrientation”/>


with a lower priority declaration :

<activity

  android:name=”com.foo.bar.ActivityOne”

android:screenOrientation=”landscape”

android:theme=”@olddogtheme”

android:windowSoftInputMode=”stateUnchanged”

android:exported=”true”/>


will result in :

<activity

  android:name=”com.foo.bar.ActivityOne”

android:hardwareAccelerated=”true”

android:windowSoftInputMode=”stateUnchanged”

android:exported=”true”/>


Enforcing an attribute value


Implicitly, all declared attributes are virtually annotated with a “strict” merging policy so if two elements eligible for merging each have the same attribute with a different value, this is a conflict that need to be explicitly addressed.


so, a higher priority declaration

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@newdogtheme”/>


with a lower priority declaration :

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@olddogtheme”/>


and a higher priority declaration

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@newdogtheme”

tools:strict=”theme”/>


with a lower priority declaration :

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@olddogtheme”/>


are strictly equivalent and will fail to merge correctly until a tools:replace=”theme” is added.

Mixed operations


If the user wants to remove some attributes and override others while conserving another set of original attributes, just add all markers side by side.


for example :

<activity

  android:name=”com.foo.bar.ActivityOne”

android:windowSoftInputMode=”stateUnchanged”

android:theme=”@theme1”

tools:remove=”android:exported, android:screenOrientation”

tools:replace=”android:theme”/>


with a lower priority declaration :

<activity

  android:name=”com.foo.bar.ActivityOne”

android:screenOrientation=”landscape”

android:theme=”@olddogtheme”

android:exported=”true”/>


will result in :

<activity

  android:name=”com.foo.bar.ActivityOne”

android:theme=”@theme1”

android:windowSoftInputMode=”stateUnchanged”/>


Note that if the lower priority declaration contained android:windowSoftInputMode or any attribute that is not explicitly tagged for removal or replacement, a build error need to be generated.

Element markers examples

Element Removal

To remove an element from any library, declare in the higher priority document

<activity-alias

android:name=”foo.bar.alias”>

<meta-data

android:name=”zoo”

tools:node=”remove”/>

</activity-alias>


merged with :

<activity-alias

android:name=”foo.bar.alias”>

<meta-data

android:name=”zoo”

android:value=”@string/bear”/>

</activity-alias>


will yield :

<activity-alias

android:name=”foo.bar.alias”>

</activity-alias>


All Elements removal

To remove all elements of a certain type from any library, declare in the higher priority document

<activity-alias

android:name=”foo.bar.alias”>

<meta-data  

tools:node=”removeAll”/>

</activity-alias>


merged with :

<activity-alias

android:name=”foo.bar.alias”>

<meta-data

android:name=”zoo”

android:value=”@string/bear”/>

<meta-data

android:name=”cage”

android:value=”@string/iron”/>

</activity-alias>


will yield :

<activity-alias

android:name=”foo.bar.alias”

</activity-alias>


Element substitution

<activity-alias

android:name=”foo.bar.alias”

tools:node=”replace”>

<meta-data

android:name=”zoo”/>

</activity-alias>


merged with:

<activity-alias

android:name=”foo.bar.alias”>

<meta-data

android:name=”cage”

android:value=”@string/iron”/>

</activity-alias>


will yield:

<activity-alias

android:name=”foo.bar.alias”>

<meta-data

android:name=”zoo”

tools:node=”remove”/>

</activity-alias>

Selector examples

Using the package name to select the library. Here we have 3 libraries merging into a main manifest file.

Main Manifest

<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   xmlns:tools="http://schemas.android.com/tools"

   package="com.example.main">


   <permission

         android:name="permissionOne"

         tools:node="remove"

         tools:selector="com.example.lib1">

   </permission>

   <permission

         tools:node="removeAll"

         tools:selector="com.example.lib3">

   </permission>

   <permission

            android:name="permissionThree"

            android:protectionLevel="signature"

            tools:node="replace">

   </permission>


</manifest>


with library 1 


<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.lib1">


   <permission android:name="permissionOne"

            android:protectionLevel="signature">

   </permission>

   <permission android:name="permissionTwo"

            android:protectionLevel="signature">

   </permission>

</manifest>


and library 2  


<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.lib2">


   <permission android:name="permissionThree"

            android:protectionLevel="normal">

   </permission>

   <permission android:name="permissionFour"

            android:protectionLevel="normal">

   </permission>

</manifest>


and library 3 


<manifest

   xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.lib2">


   <permission android:name="permissionFive"

            android:protectionLevel="normal">

   </permission>

</manifest>


will yield:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.main" >


   <permission

       android:name="permissionThree"

       android:protectionLevel="signature" >

   </permission>

   <permission

       android:name="permissionTwo"

       android:protectionLevel="signature" >

   </permission>

   <permission

       android:name="permissionFour"

       android:protectionLevel="normal" >

   </permission>


</manifest>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值