Android Official Technical Documentation Translation - List Merge [Translation]

The translation work is time-consuming and labor-intensive. If you think the translation of this article is OK, please click "like" at the end of the article; if there are any mistakes, please correct me. Thanks.

This new merge tool was introduced in version 0.10 of the gradle android plugin. As of version 0.11, the gradle plugin uses this merge tool by default.

If you want to revert to using the old manifest merge tool, you can add the following configuration to your build.gradle:

android {
    useOldManifestMerger true
}

Manifest file sorting

In general, there are three types of manifest files that need to be combined into a final application manifest, listed here in order of priority:

Manifest file specified by Product flavors and build type.
The application's main manifest file.
The manifest file for the class library.

The first type of manifest file typically overrides the contents of the manifest, as it is specific to the application for a particular delivery. A third type of manifest file is then usually merged into the main manifest produced in the previous step. The rules for merging depend on each node type and can be changed using the "tools:" namespace attribute.

Due to multiple product flavors and build types, the combined result of these manifest file merges may be a matrix. However, for each assembly step, only one value of each flavor group and build type can be selected, resulting in a potentially sorted list of manifest files overriding the main manifest file.

For example, the following FlavorGroups: abi, density, API, Prod/Internal result in the following matrix of possible flavors:

ABI Density API Prod/Internal
x86 mdpi 9 prod
arm high 14 internal
mips xhigh 15
xxhigh

This results in 3x4x3x2 possible combinations. However, for each execution assembly, there can only be a falvor in one group, and the attributes defined in the flavor group of the original build.gradle form a list of manifest files that may be merged, and this list is from high priority to low Prioritize.

For example, building the x86-high-15-prod variant looks for the following manifest files to merge

x86/AndroidManifest.xml
high/AndroidManifest.xml
15/AndroidManifest.xml
internal/AndroidManifest.xml

In this ordered list, each file has a priority according to its order in the list, and the merge tool will use this priority to decide which XML element or attribute will override a lower priority setting.

Therefore, the input to the merge tool will be as follows:

  • An ordered list of flavor or build type manifest files, which are often referenced as manifests of flavors.
  • master manifest file
  • An ordered list of manifest files declared (or transitively) by some library.
  • Injected values ​​for placeholders and XML generation

Android manifest file merging
Each element in a manifest file can be identified by its element type (such as activity, intent-filter) and an optional key value (key value). Some elements, such as "activity", must have a key, because there may be multiple such elements in an AndroidManifest.xml. Other elements, like "application", do not require a key because there can only be one such element.

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

Merged activities are always between two elements of the same type, one from a higher-priority manifest file and one from a lower-priority manifest file. Each merged activity has a default behavior, which will be described later. Additionally, each default merged activity on a node or a specified attribute may be overridden by the specified flags of the including tool.

During the merging process, the merging results of each node will also be recorded, which will be described in the "Log" section.

The process of merging elements and attributes


Implicit declarations
Some properties will have default values ​​(Defaults are defined in the online documentation). When a higher-priority element does not define an attribute with a default value of X, if a lower-priority element also happens to define an attribute with the same value X, then this attribute will still be added to the merged element. (of course its value is X), because it represents an explicit choice of the value of this property by the class library, so that it does not consider the default value as its value, but sets a correct value for a certain characteristic (in the form of in case the default value is changed).

Most properties with default values ​​will be ignored by the manifest merge tool if a value is already defined in a low-priority property; the defined value will be merged because between the default value and a set value, There is no conflict. In the resulting merged element, this attribute will be set to the set value.

However, there are a few exceptions listed below:

<uses-feature android:required> The default value is true. When merging with other attributes, the "OR" merging strategy will be used. This is because if any library requires the feature, the generated application will also need it.
<uses-library android:required> 同 uses-feature:required。
<uses-sdk android:minSdkVersion>

The default value is 1.

The version of the higher priority file will be used, but importing a newer version of the library will generate an error. 

<uses-sdk android:maxSdkVersion> 同 uses-sdk:minSdkVersion
<uses-sdk android:targetSdkVersion> 同 uses-sdk:minSdkVersion

Automatic upgrades
When importing a library that has a lower target SDK version than the project, it may need to explicitly grant permissions (and possibly other changes) in order for the library to function correctly on future runs. This will be done automatically by the manifest merge tool.

Placeholder Support
When an attribute value contains a placeholder (see format below), the merge tool will replace the placeholder's value with an injected value. The injected value is defined in build.gradle.

The syntax for the placeholder value is ${name}, since the @ sign is reserved for links. After the final file merge happens, and before the merged android manifest output is generated, all values ​​with placeholders will be replaced with injected values. If the variable name is unknown, the build will fail.

Placeholder strings can have a prefix or suffix to replace only part of the content.

Example:

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

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

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

The value of the implicit placeholder ${applicationId} will be automatically provided by the existing build.gradle applicationId value.

Example:

<activity

    android:name=".Main">

    <intent-filter>
        <action android:name="${applicationId}.foo">
        </action>
    </intent-filter>

</activity>

Pass the following gradle declaration:

android {

   compileSdkVersion 19
   buildToolsVersion "19.0.2"

   productFlavors {
       flavor1 {
           applicationId = "com.android.tests.flavorlib.app.flavor1"
       }
   }

    ...
}

Once merged, <action android:name> will be

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

For custom placeholder replacements, the following DSL can be used to configure placeholder values:

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

It will replace the placeholders in the following declarations:

<activity android:name=".MainActivity"android:label="${activityLabel}" >

Common descriptions of merging strategies
XML merging may be at the node level or at the attribute level.

At the node level, the default merge strategy is to merge attributes and child elements as long as there are no conflicts. A conflict occurs when two elements with the same ID have the same attribute and the attribute has a different value.

for example:

<activity

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

android:theme=”@theme1”/>

There is no conflict when merged with the following declarations:

<activity

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

android:screenOrientation=”landscape/>

same

<activity

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

android:theme=”@theme1”/>

There is also no conflict when combined with the declaration below, because the "theme" attribute defined in both elements has the same value.

<activity

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

android:theme="@theme1"

android:screenOrientation=”landscape/>

but,

<activity

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

android:theme=”@theme1”/>

A conflict occurs when merging with the following declaration, because the values ​​of the "theme" attribute defined in the two elements are not the same.

<activity

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

android:theme=”@theme2”

android:screenOrientation=”landscape/>

Now, each element can have sub-elements and rules for matching attributes that will follow the same basic principle that sub-elements with the same id will match together. If a child element exists in only one of the parent elements, there is no conflict.

Flags
A flag is a special attribute in the tools namespace that describes the decision to be taken on how to resolve conflicts.

All tags belong to the Android Tools namespace, so you must declare that namespace in any AndroidManifest.xml that contains at least one tag:

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>

Some flags must be explicitly added to direct the Manifest Marger when there are conflicts between elements to be merged. At the node level, the tools:node attribute should be used, and at the attribute level, the tools:attr attribute should be used.

tools:node flag
When a node has a conflict and needs to be resolved, there should be a tools:node="maker_value" attribute.

<tools:node> attribute value Behavior of the manifest merge tool
<tools:node="merge"> This is the implicit default mode for node merging, where nodes are merged as long as they do not conflict.
<tools:node="replace"> Replace the lower priority declaration with the annotated one.
<tools:node="strict"> Will cause the build to fail when another node with the same identity exists and is not strictly equal.
<tools:node="merge-only-attributes"> Only attributes from lower-priority declarations are merged.
<tools:node="remove"> Removes the annotated element from the generated XML. Regardless of possible conflicts, lower priority declarations are not merged.
<tools:node="removeAll"> Removes all elements of the same node type (not essential).

The tools:attr tag
In any particular element, there may be many tag-related attributes used to resolve conflicts between all attributes.

<tools:strict=”x, y, z”> The default implicit mode for properties will generate an error when attempting to combine declarations of lower-priority properties with different values.
<tools:remove=”x, y, z”> When merging, remove the x, y, z attributes from any lower-priority declarations.
<tools:replace=”x, y, z”> Replace the x, y, z attribute values ​​of any lower-priority declarations with the provided values ​​(must be on the same node).

Selectors
Every tools:node or tools:attr declaration can be extended with a tools:selector attribute, which is contextual information about whether the merge strategy should be applied to the current low-priority XML description. For example, it can be useful when removing a permission is only required in a specific library, but not in any library:   

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

tools:overrideLibrary flag
This is a special flag that is only used with the use-sdk declaration to override whether the library is imported when the minimum SDK version of the imported library is newer than the minimum SDK version of the application. Write.

Without such a flag, the manifest merge will fail. This flag will allow the user to choose which libraries can be imported regardless of the minimum SDK version. 

For example, in the main android manifest: 

<uses-sdk android:targetSdkVersion="14" android:minSdkVersion="2"

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

will allow a 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 every action or decision during manifest merging needs to be

  • recorded
  • Formatted so that computers can parse
  • sorted by node (since multiple attributes of a node may result in several merge decisions)

Blame
can get a "blame" type of output with some indication of where the element or attribute is, to limit every element and attribute produced in the merged XML.

Merge Strategy
Each element type has a specific default merge strategy attached to it. For example, most element types like activity or application have a default merge strategy where all attributes and child elements are merged (assuming there are no conflicts) into the produced element. However, for other elements, such as top-level manifests, the default merge strategy is to only merge child elements. This means that the attributes of the manifest element of the lower priority AndroidManifest.xml are not eligible to be merged.

Each element can also have or not have a key associated with it. For example, application has no key, and there can only be one <application> element in each AndroidManifest.xml. Most keyed elements use the "android:name attribute" to represent their key-value, or something, etc.

Merge
Attributes that do not conflict will be merged, and child elements will be merged according to their respective merge strategies.

Merge only child elements
Attributes will not be merged, only child elements will be merged according to their respective merge policies.

Always Merge
Always keep elements "as is" and add to the common parent element in the resulting merged file.

List of element merge strategies and keys

node type merge strategy 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 merge Action and categories android: name attribute of child elements. Multiple claims for the same key are allowed.
manifest Merge only child elements no key
meta-data merge android:name attribute
path-permission merge no key
permission-group 合并 android:name 属性
permission 合并 android:name 属性
permission-tree 合并 android:name 属性
provider 合并 android:name 属性
receiver 合并 android:name 属性
screen 合并 属性 screenSize
service 合并 android:name 属性
supports-gl-texture 合并 android:name 属性
supports-screen 合并 没有键
uses-configuration 合并 没有键
uses-feature 合并 首先是属性名称,如果不存在,则接着是 glEsVersion 属性
uses-library 合并 android:name 属性
uses-permission 合并 android:name 属性
uses-sdk 合并 没有键
自定义元素 合并 没有键

包名称智能替换
有些属性是包依赖属性,意思就是说这些属性支持通过由清单节点中的package属性提供的包设置,对部分完全限定的类名称的智能替换。

下面描述的每个属性都可以有一个局部的类名称,这个类名称是以一个点或不包含任何点开头的。

示例:

<manifest

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

   package="com.example.app1">

   <application>

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

   </application>

</manifest>

将被扩充成:   

<application>

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

</application>

这是独立于build.gradle里的任何包设置的(译者注,关于package和applicationId可以看一下我的系列博客中的另一篇文章的介绍)。例如,你build.gradle 包含以下内容:

android {

   compileSdkVersion 19

   buildToolsVersion "19.0.2"

   productFlavors {

       flavor1 {

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

       }

}

扩充的结果仍然是

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

如果你需要让注入的值作为扩充的属性值,可以使用 ${applicationId} 占位符,例如:

<manifest

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

   package="com.example.app1">

   <application>

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

   </application>

</manifest>

下面是可以使用这种智能替换的能力包独立属性的列表:

节点类型 属性的本地名称
activity name, parentActivityName
activity-alias name, targetActivity
application name, backupAgent
instrumentation name
provider name
receiver name
service name

属性标记示例


重写来自库的属性
使用 tools:replace="x, y, z" 将会重写从外部库的activity 的XML声明中导入的 x,y,z 属性。

更高级别的声明

<activity

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

android:screenOrientation="portrait"

android:theme="@theme1"

tools:replace=”theme”/>

和一个较低优先级的声明:

<activity

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

android:theme="@olddogtheme"

android:windowSoftInputMode="stateUnchanged"

android:exported="true" >

将产生:

<activity

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

android:screenOrientation="portrait"

android:theme="@theme1"

android:windowSoftInputMode="stateUnchanged"

android:exported="true"/>

删除来自库的属性。
使用 tools:remove="x, y, z" 将会在产生的XML中删除 x,y,z 属性的声明。

更高优先级的声明

<activity

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

android:hardwareAccelerated="true"

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

和一个较低优先级的声明:

<activity

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

android:screenOrientation="landscape"

android:theme="@olddogtheme"

android:windowSoftInputMode="stateUnchanged"

android:exported="true"/>

将产生:

<activity

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

android:hardwareAccelerated="true"

android:windowSoftInputMode="stateUnchanged"

android:exported="true"/>

强制更新属性值
毫无疑问,所有声明属性几乎都带有“strict”的合并策略,所以如果两个要合并的元素都有一个同样名称的属性但值却不同,就是一个需要明确解决的冲突。

所以,一个较高优先级的声明

<activity

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

android:theme=”@newdogtheme”/>

和一个较低优先级的声明:

<activity

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

android:theme=”@olddogtheme”/>

与一个较高优先级的声明

<activity
    android:name="com.foo.bar.ActivityOne"
    android:theme=”@newdogtheme”
    tools:strict=”theme”/>

和一个较低优先级的声明:

<activity

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

    android:theme=”@olddogtheme”/>

是完全等价的,并且都将不能正确地合并,除非添加一个 tools:replace="theme" 的属性。

混合操作
如果用户想要删除某些属性且重写其他属性同时保存另一组的原始属性,只需依次添加所有标记。

例如:

<activity

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

 android:windowSoftInputMode="stateUnchanged"

 android:theme="@theme1"

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

 tools:replace="android:theme"/>

和一个较低优先级的声明:

<activity

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

 android:screenOrientation="landscape"

 android:theme="@olddogtheme"

 android:exported="true"/>

将产生:

<activity

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

 android:theme="@theme1"

 android:windowSoftInputMode="stateUnchanged" />

需要注意的是,如果低优先级的声明中包含 android:windowSoftInputMode或者未明确标记为删除或替换的任何属性,将生成一个构建错误。

元素标记示例


移除元素
如果要删除任何一个库的某个元素,需要在更高优先级的文件中声明

<activity-alias

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

<meta-data

android:name="zoo"

tools:node="remove"/>

</activity-alias>

与下面进行合并

<activity-alias

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

<meta-data

android:name="zoo"

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

</activity-alias>

将产生:

<activity-alias

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

</activity-alias>

移除所有元素
如果要作任何一个库的一个特定类型的所有元素,需要在更高优先级的文件中声明

<activity-alias

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

<meta-data  

tools:node="removeAll" />

</activity-alias>

与下面进行合并

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

将产生:

<activity-alias

android:name="foo.bar.alias"

</activity-alias>

元素替换

<activity-alias

android:name="foo.bar.alias"

tools:node="replace">

<meta-data

android:name="zoo"/>

</activity-alias>

与下面进行合并

<activity-alias

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

<meta-data

android:name="cage"

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

</activity-alias>

将产生:

<activity-alias

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

<meta-data

android:name="zoo"

tools:node="remove"/>

</activity-alias>

选择器示例
使用包名称来选择库这里,我们有三个库要合并进一个主清单文件中。主清单

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

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

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

及库3 

<manifest

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

   package="com.example.lib2">

   <permission android:name="permissionFive"

            android:protectionLevel="normal">

   </permission>

</manifest>

将产生:

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

日志记录不会被组织为最终产生输出文件的事件和决策的线性集合。相反,为了方便开发人员,日志文件将按输入文件中发生冲突的顶级XML节点来进行组织 (无论当我们想想文件的节点删除的时候,它是否存在 于输出文件中)。

日志记录:
一个日志记录既是一个节点记录 (描述在该特定节点上采取的所有操作),也是一个包含错误消息和警告的消息记录。

日志文件 = 日志记录*

日志记录 = 节点记录 | 消息

消息

消息=文件:行号:列号 严重性:\n描述

描述=(\t内容\n)*

名称
file 生成日志条目的输入文件
line-number 生成日志条目的输入的文件行号
column-number 生成日志条目的输入文件列号
严重性 Error, Warning, Info
description 日志条目有效载荷(译者注:有效载荷即记载着信息的那部分数据)

示例

/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

节点记录

名称
node-type     
XML 节点类型
node-key 节点的键的属性值
record-type [Action |Log ] *

node-type#node-key\n

\t(node_action:Action)*

\t\t(attribute_action:Action)*

操作格式

名称
action-type added | rejected | implied
target node | attribute
target-name 节点的键名称或属性名称
origin 原始值的位置

示例:

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

构建错误
当发生构建错误时,则应显示特定节点失败的日志,并在后面有一段向用户描述的错误消息。举个例子:

更高优先级的声明

<activity

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

 android:screenOrientation="portrait"

 android:theme=”@theme1”/>

和一个较低优先级的声明:

<activity

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

 android:screenOrientation=”landscape/>

会同时产生日志文件和输出结果(能让人类,计算机以及IDE都能识别的确切格式还未确定)。

/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

References:

Android official technical documentation translation - list merge

Guess you like

Origin blog.csdn.net/rsp19801226/article/details/131101106