android.com/,Manifest Merger

本文详细介绍了Android应用中多个构建变体和库如何合并其AndroidManifest.xml文件的过程。它涉及到产品风味、构建类型特定的清单文件、主要应用清单和库清单的合并策略。在合并过程中,针对每个元素和属性,存在默认的合并规则,如属性的覆盖、默认值的处理、冲突解决和注解标记的使用。此外,还讨论了如何通过`tools:replace`、`tools:remove`和`tools:node`等属性来控制合并行为,以及如何处理权限的隐式添加。最后,提到了日志记录和错误处理机制,用于记录合并过程中的决策和冲突解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

Product flavors and build types specific manifest files.

Main manifest file for the application.

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

x86/AndroidManifest.xml

high/AndroidManifest.xml

15/AndroidManifest.xml

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

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.

Same as uses-feature:required.

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.

Same as uses-sdk:minSdkVersion

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 :

android:name=".Main">

with the following gradle declaration :

android {

compileSdkVersion 19

buildToolsVersion "19.0.2"

productFlavors {

flavor1 {

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

}

}

Once merged, the will be

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 :

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:

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

android:theme=”@theme1”/>

merging with the following declaration will not generate a conflict:

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

android:screenOrientation=”landscape/>

same for

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.

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

android:theme=”@theme1”

android:screenOrientation=”landscape/>

however,

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.

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 :

Example:

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

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

android:icon="@drawable/icon"

android:label="@string/app_name"

tools:replace=”icon, label”/>

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.

attribute value

manifest merger action

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

Replace the lower priority declaration with the annotated one.

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

Only merge attributes from the lower priority declaration.

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

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.

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

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

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:

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 :

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

will allow the library with the following manifest to be imported without error :

package="com.example.lib1">

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 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:labelADDED from ManifestMerger2Test0_main.xml:61:13

android:iconADDED from ManifestMerger2Test0_main.xml:62:13

android:nameADDED 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

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

android:screenOrientation=”portrait”

android:theme=”@theme1”/>

with a lower priority declaration :

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

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

package="com.example.app1">

will be expanded into :

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

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

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

package="com.example.app1">

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

Implicit permissions addition

When importing a library with a targetSdkVersion lower than the importing application targetSdkVersion, some permissions maybe automatically added to the resulting merged manifest file. This is necessary since such libraries targeted runtimes where such permissions wereimplicitlygranted. Therefore declaring such permission was not necessary. However in more recent Android releases, these permissions are not automatically granted. Therefore, a library targeting such old runtime without the permissions would not work properly once merged in the application targeting a more recent runtime. For instance, an application with a targetSdkVersion of 16 importing a library with a targetSdkVersion of 2 will get the WRITE_EXTERNAL_STORAGE permission added. See the table below for a complete list.

permissionreason

WRITE_EXTERNAL_STORAGEAdded when importing a library with a targetSdkVersion < 4

READ_EXTERNAL_STORAGEAdded when importing a library that declared WRITE_EXTERNAL_STORAGE

READ_PHONE_STATEAdded when importing a library with a targetSdkVersion < 4

READ_CALL_LOGAdded when importing a library with a targetSdkVersion < 16 and using READ_CONTACTS permission

WRITE_CALL_LOGAdded when importing a library with a targetSdkVersion < 16 and using WRITE_CONTACTS permission

Remember that the targetSdkVersion is defaulted to minSdkVersion when notexplicitlydeclared. When no element is present, the minSdkVersion is itself defaulted to 1.

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

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

android:screenOrientation=”portrait”

android:theme=”@theme1”

tools:replace=”theme”/>

with a lower priority declaration :

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

android:theme=”@olddogtheme”

android:windowSoftInputMode=”stateUnchanged”

android:exported=”true”>

will result in :

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

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

android:hardwareAccelerated=”true”

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

with a lower priority declaration :

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

android:screenOrientation=”landscape”

android:theme=”@olddogtheme”

android:windowSoftInputMode=”stateUnchanged”

android:exported=”true”/>

will result in :

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, ahigher priority declaration

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

android:theme=”@newdogtheme”/>

with a lower priority declaration :

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

android:theme=”@olddogtheme”/>

and ahigher priority declaration

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

android:theme=”@newdogtheme”

tools:strict=”theme”/>

with a lower priority declaration :

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 :

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 :

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

android:screenOrientation=”landscape”

android:theme=”@olddogtheme”

android:exported=”true”/>

will result in :

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

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

android:name=”zoo”

tools:node=”remove”/>

merged with :

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

android:name=”zoo”

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

will yield :

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

All Elements removal

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

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

tools:node=”removeAll”/>

merged with :

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

android:name=”zoo”

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

android:name=”cage”

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

will yield :

android:name=”foo.bar.alias”

Element substitution

android:name=”foo.bar.alias”

tools:node=”replace”>

android:name=”zoo”/>

merged with:

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

android:name=”cage”

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

will yield:

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

android:name=”zoo”

tools:node=”remove”/>

Selector examples

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

Main Manifest

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

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

package="com.example.main">

android:name="permissionOne"

tools:node="remove"

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

tools:node="removeAll"

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

android:name="permissionThree"

android:protectionLevel="signature"

tools:node="replace">

with library 1

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

package="com.example.lib1">

android:protectionLevel="signature">

android:protectionLevel="signature">

and library 2

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

package="com.example.lib2">

android:protectionLevel="normal">

android:protectionLevel="normal">

and library 3

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

package="com.example.lib2">

android:protectionLevel="normal">

will yield:

package="com.example.main" >

android:name="permissionThree"

android:protectionLevel="signature" >

android:name="permissionTwo"

android:protectionLevel="signature" >

android:name="permissionFour"

android:protectionLevel="normal" >

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值