目录html
Markdown版本笔记 | 个人GitHub首页 | 个人博客 | 个人微信 | 个人邮箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
官方文档node
Merge multiple manifest filesandroid
APK 文件只能包含一个 AndroidManifest.xml
文件,但 Android Studio 项目能够包含多个文件[may contain several provided by the main source set
, build variants
, and imported libraries
]。所以,在构建应用时,Gradle 构建会将全部清单文件合并到一个封装到 APK 的清单文件中[merges all manifest files into a single manifest file that's packaged into your APK]。git
清单合并工具经过遵循某些合并启发式算法[merge heuristics],并遵照您经过特殊 XML 属性定义的合并首选项[merge preferences],来合并各个文件中的全部 XML 元素 。 本页介绍清单合并的工做方式以及如何应用合并首选项来解决合并冲突[resolve merge conflicts]。github
提示: 使用 Merged Manifest 视图预览合并清单的效果并找出冲突错误。算法
Merge priorities微信
合并工具根据每一个清单文件的优先级将全部清单文件按顺序
合并到一个文件中。 例如,若是您有 3 个清单文件,则会先将优先级最低的清单合并到优先级第 2 高的清单中,而后再将合并后的清单合并到优先级最高的清单中(如图 1 所示)。app
有 3 种基本的清单文件能够互相合并,它们的合并优先级以下(按优先级由高到低的顺序):less
一、清单文件构建变体 Manifest file for your build variant
若是您的变体有多个源集,则其manifest优先级以下:编辑器
Build variant
manifest (如 src/demoDebug/)Build type
manifest (如 src/debug/)Product flavor
manifest (如 src/demo/)若是您使用的是 flavor dimensions,清单优先级将与每一个维度在 flavorDimensions 属性中的列示顺序(按优先级由高到低的顺序排列)对应。
二、app模块的主清单文件 Main manifest file for the app module
三、所包括库中的清单文件 Manifest file from an included library
若是您有多个库,则其清单优先级与依赖顺序(库出如今 dependencies 块中的顺序)匹配。
重要说明: build.gradle 文件中的构建配置将替换合并清单文件中的任何对应属性。 例如,build.gradle 文件中的minSdkVersion 将替换
清单元素中的匹配属性。为了不混淆,您只需省去 元素并在 build.gradle 文件中定义这些属性。For more details, see Configure Your Build.
Merge conflict heuristics
合并工具能够在逻辑上将一个清单中的每一个 XML 元素与另外一个清单中的对应元素相匹配。(有关匹配如何进行的详细信息,请参阅有关 合并策略 的附录)。
若是优先级较低的清单中的元素与优先级较高的清单中的任何元素均不匹配,则该元素将被添加至合并清单。 可是,若是有匹配元素,则合并工具会尝试将其中的全部属性合并到相同元素中。若是工具发现两个清单包含相同属性,但值不相同
,则会出现合并冲突。
下表 1 描述了合并工具尝试将全部属性合并到同一元素时可能出现的结果。
高优先级属性 | 低优先级属性 | 属性的合并结果 |
---|---|---|
没有值 | 没有值 | 没有值(使用默认值) |
没有值 | 值 B | 值 B |
值 A | 没有值 | 值 A |
值 A | 值 A | 值 A |
值 A | 值 B | 冲突错误—必须添加一个 合并规则标记 |
可是,在某些状况下,合并工具会采起其余行为方式以免合并冲突:
<manifest>
元素中的属性不合并--仅使用优先级最高的清单中的属性
。<uses-feature>
和 <uses-library>
元素中的 android:required
属性使用 OR 合并,所以若是出现冲突(值不一致),系统将应用 "true" 并始终包括某个清单所需的功能或库[the feature or library required by one manifest is always included]。<uses-sdk>
元素始终使用优先级较高的清单中的值
,但如下状况除外:
minSdkVersion
值较高,您必须应用 overrideLibrary
合并规则[an error occurs unless you apply the overrideLibrary merge rule]。targetSdkVersion
值较低,合并工具将使用高优先级清单中的值,但也会添加任何须要的系统权限
,以确保所导入的库继续正常工做(适用于较高的 Android 版本具备更多权限限制的状况)。 如需了解有关此行为的详细信息,请参阅有关 隐式系统权限[implicit system permissions] 的部分。<intent-filter>
元素。每一个元素都被视为惟一元素,并添加至合并清单中的经常使用父元素[the common parent element in the merged manifest]。对于属性之间的全部其余冲突,您将收到一则错误,而且必须经过在高优先级清单文件中添加特殊属性
来指示合并工具如何解决此错误。
不要依赖于默认属性值 Do not depend on default attribute values
因为全部惟一属性[unique attributes]都合并到相同元素中,若是高优先级清单实际上依赖于属性的默认值而不须要声明,则可能会致使意外结果。
例如,若是高优先级清单不声明android:launchMod
e 属性,则会使用 "standard" 的默认值;但若是低优先级清单声明此属性具备其余值,该值将应用于合并清单(替代默认值)。
所以,您应该定期望明肯定义每一个属性。(每一个属性的默认值都会记录在 Manifest reference 中)。
Merge rule markers
合并规则标记是一个 XML 属性,可用于表达您对关于如何解决合并冲突或删除不须要的元素和属性的首选项。您能够对整个元素或只对元素中的特定属性应用标记。
合并两个清单文件时,合并工具会在高优先级清单文件中寻找这些标记
。
全部标记均属于 Android tools 命名空间,所以您必须先在 <manifest>
元素中声明此命名空间,以下文所示:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp" xmlns:tools="http://schemas.android.com/tools">
Node markers
要向整个 XML 元素(给定清单元素中的全部元素及其全部子标记)应用合并规则,请使用如下属性:
tools:node="merge"
若是使用合并冲突启发式算法
时没有冲突,则合并
此标记中的全部属性以及全部嵌套元素
。这是元素的默认行为。
低优先级清单:
<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.ActivityOne” android:screenOrientation=”portrait” tools:node="merge”> </activity>
合并的清单结果:
<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>
仅合并此标记中的属性,不合并嵌套元素
。
低优先级清单:
<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>
高优先级清单:
<activity android:name=”com.example.ActivityOne” android:screenOrientation=”portrait” tools:node="merge-only-attributes”/>
合并的清单结果:
<activity android:name=”com.example.ActivityOne” android:screenOrientation=”portrait” android:windowSoftInputMode=”stateUnchanged”/>
彻底替换低优先级元素。 也就是说,若是低优先级清单中有匹配元素,请将其忽略并彻底按照其在此清单中显示样子来使用该元素。
低优先级清单:
<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>
高优先级清单:
<activity-alias android:name=”com.example.alias” tools:node=”replace”> <meta-data android:name=”fox” android:value=”@string/dingeringeding”/> </activity-alias>
合并的清单结果:
<activity-alias android:name=”com.example.alias”> <meta-data android:name=”fox” android:value=”@string/dingeringeding”/> </activity-alias>
从合并清单中删除此元素。 尽管您彷佛应该仅删除此元素,但若是您发现合并清单中有不须要的元素,则必须使用此选项。该选项由不受您控制的低优先级清单(如导入的库)提供。
低优先级清单:
<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>
高优先级清单:
<activity-alias android:name=”com.example.alias”> <meta-data android:name=”cow” tools:node=”remove”/> </activity-alias>
合并的清单结果:
<activity-alias android:name=”com.example.alias”> <meta-data android:name=”duck” android:value=”@string/quack”/> </activity-alias>
与 tools:node="remove"
相似,但它会删除同一父元素内与此元素类型相匹配
的全部元素。
低优先级清单:
<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>
高优先级清单:
<activity-alias android:name=”com.example.alias”> <meta-data tools:node=”removeAll”/> </activity-alias>
合并的清单结果:
<activity-alias android:name=”com.example.alias”/>
当此元素在低优先级清单中的状况与在高优先级清单中的状况不彻底匹配时生成构建故障(除非已经过其余合并规则标记解决)。 这将替换默认的合并冲突启发式算法。 例如,若是低优先级清单仅包括额外属性[an extra attribute],则构建将会失败(而默认行为会向合并清单添加额外属性)。
低优先级清单:
<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.ActivityOne” android:screenOrientation=”portrait” tools:node="strict”/>
这会生成清单合并错误。两个清单元素在严格模式下也彻底没法区分。 所以,您必须应用其余合并规则标记来解决这些差别。
要改成仅向清单标记中的 特定属性[specific attributes] 应用合并规则,请使用属性标记。每一个属性接受一个或多个属性名称(包括属性命名空间),并以逗号分隔。
从合并清单中删除指定属性。尽管您彷佛能够仅删除这些属性,但若是低优先级清单文件不包括这些属性,并且您但愿确保它们不归入合并清单,则必须使用此选项。
低优先级清单:
<activity android:name=”com.example.ActivityOne” android:windowSoftInputMode=”stateUnchanged”/>
高优先级清单:
<activity android:name=”com.example.ActivityOne” android:screenOrientation=”portrait” tools:remove=”android:windowSoftInputMode”/>
合并的清单结果:
<activity android:name=”com.example.ActivityOne” android:screenOrientation=”portrait”/>
将低优先级清单中的指定属性替换为此清单中的属性。换言之,始终保持高优先级清单的值。
低优先级清单:
<activity android:name=”com.example.ActivityOne” android:theme=”@oldtheme” android:exported=”false” android:windowSoftInputMode=”stateUnchanged”>
高优先级清单:
<activity android:name=”com.example.ActivityOne” android:theme=”@newtheme” android:exported=”true” android:screenOrientation=”portrait” tools:replace=”android:theme,android:exported”>
合并的清单结果:
<activity android:name=”com.example.ActivityOne” android:theme=”@newtheme” android:exported=”true” android:screenOrientation=”portrait” android:windowSoftInputMode=”stateUnchanged”>
当这些属性在低优先级清单中的状况与在高优先级清单中的不彻底匹配时生成构建故障。 这是全部属性的默认行为,具备 合并冲突启发式算法中介绍的特殊行为的属性除外。
低优先级清单:
<activity android:name=”com.example.ActivityOne” android:screenOrientation=”landscape”/>
高优先级清单:
<activity android:name=”com.example.ActivityOne” android:screenOrientation=”portrait” tools:strict="android:screenOrientation”/>
这会生成清单合并错误。 您必须应用其余合并规则标记来解决冲突。
请谨记:这是默认行为,所以若是您删除
tools:strict="screenOrientation”
,上面的示例将具备相同的结果。
您也能够对一个元素同时应用多个标记,以下所示。
低优先级清单:
<activity android:name=”com.example.ActivityOne” android:theme=”@oldtheme” android:exported=”false” android:allowTaskReparenting="true" android:windowSoftInputMode=”stateUnchanged”>
高优先级清单:
<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”>
合并的清单结果:
<activity android:name=”com.example.ActivityOne” android:theme=”@newtheme” android:exported=”true” android:allowTaskReparenting="true" android:screenOrientation=”portrait”>
Marker selector
若是您想仅对某个特定的导入库
应用合并规则标记,请添加具备库包名称的 tools:selector
属性。
例如,对于下面的清单,仅在低优先级清单文件来自 com.example.lib1
库时应用 remove
合并规则。
<permission android:name="permissionOne" tools:node="remove" tools:selector="com.example.lib1">
若是低优先级清单来自其余源,系统将会忽略 remove
合并规则。
Tips:若是您将此功能与其中一个属性标记配合使用,它将应用至标记中指定的全部选项。
默认状况下,导入 minSdkVersion 值高于主清单文件的库时会出错,并且没法导入该库。 要使合并工具忽略此冲突并导入库,同时保持应用的低 minSdkVersion 值,请将 overrideLibrary 属性添加至 <uses-sdk>
标记。属性值能够是一个或多个库包名称(以逗号分隔),指明可能替换主清单的 minSdkVersion 的库。
例如,若是应用的主清单按以下所示应用 overrideLibrary:
<uses-sdk android:targetSdkVersion="22" android:minSdkVersion="2" tools:overrideLibrary="com.example.lib1, com.example.lib2"/>
则下面这个清单(来自com.example.lib1)能够合并,而且不会出现与 <uses-sdk>
标记相关的错误,且合并清单将保留应用清单中的 minSdkVersion="2"
。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.lib1"> <uses-sdk android:minSdkVersion="4"/>
Implicit system permissions
在最近的 Android 版本中,应用曾经能够自由访问的某些 Android API 已受 系统权限 限制。为了不中断预期会访问这些 API 的应用[To avoid breaking apps that expect access to these API],最近的 Android 版本容许应用在无权限的状况下继续访问这些 API,前提是它们已将 targetSdkVersion
设置为低于添加限制的版本的值。此行为有效地向应用授予了_隐式权限_,以容许访问 API。 所以,这可能会对具备不一样 targetSdkVersion
值的合并清单产生如下影响。
若是低优先级清单文件提供隐式权限的 `targetSdkVersion` 值较低,并且高优先级清单_没有_相同的隐式权限(因为其 `targetSdkVersion` 等于或高于添加限制的版本),合并工具将向合并清单_显式_添加系统权限。
例如,若是您的应用将 targetSdkVersion
设置为 4 或更高值,而且导入了将 targetSdkVersion
设置为 3 或更低值的库,合并工具会将 WRITE_EXTERNAL_STORAGE 权限添加至合并清单。 表 2 列出了能够添加至合并清单的全部可能权限。
注:若是您将应用的
targetSdkVersion
设置为 23 或更高值,则必须在应用尝试访问受这些权限保护的 API 时为任何危险权限执行运行时权限请求。 如需得到更多指导,请参阅 使用系统权限。
表 2. 合并工具可添加至合并清单的权限列表
低优先级清单声明 | 添加至合并清单的权限 |
---|---|
targetSdkVersion 是 3 或更低值 | WRITE_EXTERNAL_STORAGE, READ_PHONE_STATE |
targetSdkVersion 是 15 或更低值,而且使用 READ_CONTACTS | READ_CALL_LOG |
targetSdkVersion 是 15 或更低值,而且使用 WRITE_CONTACTS | WRITE_CALL_LOG |
Inspect the merged manifest and find conflicts
即便在构建 APK 以前,也能够预览合并清单
,具体方法是:在 Android Studio 中打开您的 AndroidManifest.xml 文件,而后单击编辑器底部的 Merged Manifest
选项卡。
Merged Manifest 视图在左侧显示合并清单的结果
,在右侧显示每一个合并清单文件的相关信息
。
从低优先级文件中合并的元素在左侧以不一样颜色突出显示,每种颜色的关键字在右侧的 Manifest Sources 下方指定。
属于构建的一部分但不构成元素或属性的清单文件列在右侧的 Other Manifest Files 下方。
要查看有关元素来源
的信息,请在左侧单击元素,详细信息将显示在右侧的 Merging Log 下方。
若是发生任何冲突,它们将显示在右侧的 Merging Errors 下方,而且包含有关如何使用合并规则标记
解决冲突的建议。错误也会打印在 Event Log 窗口中(请选择 View > Tool Windows > Event Log)。
若是您想要查看合并决策树
的完整日志,则能够在【各个模块】的 build/outputs/logs/manifest-merger-【buildVariant】-report.txt
中查找该日志文件。
Appendix: Merge policies
清单合并工具能够在逻辑上将某个清单中的每一个 XML 元素与其余清单中的对应元素相匹配。 合并工具会使用匹配关键字[match key]
匹配每一个元素,匹配关键字能够是惟一的属性值(如 android:name
或标记自己的自然惟一性(例如,只能有一个 <supports-screen>
元素)。 若是两个清单具备相同的 XML 元素,工具将采用三种合并策略中的一种来合并这两个元素:
合并[Merge]
:将全部非冲突属性合并到同一标记中,而后按其各自的合并策略合并子元素。 若是任何属性相互冲突,请使用合并规则标记将它们合并在一块儿。仅合并子项[Merge children only]
:不整合或合并属性(仅保留优先级最高的清单文件提供的属性),并按照其合并策略合并子项。保留[Keep]
:将元素“按原样”保留,而后将其添加至合并文件中的同一父元素。此策略仅在可接受相同元素的多个声明时使用。表 3. 清单元素合并策略和合并关键字 Manifest element merge policies and match keys
元素 | 合并策略 | 合并关键字 |
---|---|---|
<action> |
合并 | android:name 属性 |
<activity> |
合并 | android:name 属性 |
<application> |
合并 | 每一个 <manifest> 仅一个 |
<category> |
合并 | android:name 属性 |
<data> |
合并 | 每一个 <intent-filter> 仅 1 个 |
<grant-uri-permission> |
合并 | 每一个 <provider> 仅 1 个 |
<instrumentation> |
合并 | android:name 属性 |
<intent-filter> |
保留 | 不匹配;容许父元素内的多个声明 |
<manifest> |
合并 | 每一个文件仅 1 个 |
<meta-data> |
合并 | android:name 属性 |
<path-permission> |
合并 | 每一个 <provider> 仅 1 个 |
<permission-group> |
合并 | android:name 属性 |
<permission> |
合并 | android:name 属性 |
<permission-tree> |
合并 | android:name 属性 |
<provider> |
合并 | android:name 属性 |
<receiver> |
合并 | android:name 属性 |
<screen> |
合并 | android:screenSize 属性 |
<service> |
合并 | android:name 属性 |
<supports-gl-texture> |
合并 | android:name 属性 |
<supports-screen> |
合并 | 每一个 <manifest> 仅 1 个 |
<uses-configuration> |
合并 | 每一个 <manifest> 仅 1 个 |
<uses-feature> |
合并 | android:name 属性(若是不存在,则使用 android:glEsVersion 属性) |
<uses-library> |
合并 | android:name 属性 |
<uses-permission> |
合并 | android:name 属性 |
<uses-sdk> |
合并 | 每一个 <manifest> 仅 1 个 |
自定义元素 | 合并 | 无匹配;合并工具不了解这些信息,所以它们始终包括在合并清单中 |
2019-6-29