Android 合并清单文件 Merge multiple manifest files

Android在打包的时候会合并清单文件,这里需要知道合并的原理,本文以 

https://developer.android.com/studio/build/manifest-merge  官方文档为参考,进行翻译解读。同时也为自己记录相应的知识点。

 合并优先级

清单文件合并是按照优先级进行合并的,低优先级的清单文件内容合并到高优先级的,假如合并的时候产生了冲突就需要解决冲突,这也是我们这篇文章重点要讲述的。而优先级的高低划分原则是这样的:

主工程(又区分构建体、渠道、风味) > module(按照依赖关系) > jar 包(aar)。

gradle的相关属性会覆盖manifest中的属性,比如最小sdk,minSdkVersion 属性会以gradle中的为准,所以manifest就不要写该属性

合并冲突

当低优先级的清单文件属性合并到高优先级中,假如高优先级中没有对应的属性,则直接合并,假如有并且不一样,这时候就有了冲突,应该去解决,怎么解决?

规则

High priority attribute Low priority attribute Attribute's merged result
No value No value No value (use default value)
Value B Value B
Value A No value Value A
Value A Value A
Value B Conflict error—you must add a merge rule marker

补充规则:

针对要合并的值不一样,该如何去合并呢?这时候就要对一些不同属性的值在不同的情况具体对待?注意manifest中的任何attribute 都可以使用补充规则,

<uses-permission android:name="android.permission.INTERNET"  tools:node="merge"/>
<uses-feature android:name="android.hardware.Sensor" tools:node="remove"/>
<uses-sdk android:targetSdkVersion="Q" tools:node="merge-only-attributes"/>
<permission android:name="xx" tools:node="replace"/>
<uses-permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES" tools:node="strict"/>
..........

常用的需要合并的属性值有 
 <uses-sdk> <uses-libray> <uses-feature> <targetSdkVersion> <minSdkVersion>.Android的解决方式是使用标记,合并工具会优先在高优先级的清单文件中寻找这些标记,这一点要记住。另外,在使用标记的时候,需要在根节点manifest中添加 tools 命名空间:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp"
    xmlns:tools="http://schemas.android.com/tools">

节点标记

merge tools:node = "merge/replace/strict/remove/removeAll/merge-only-attribute"

合并规则中很重要的一个规则是节点标记,而merge 是默认行为,当manifest 各节点属性没有声明节点标记时候,就会使用默认值。现在一一解释每个值的含义

  • merge  合并该attribute 所有的属性和子属性;
  • replace 替换其他的值,使用该清单文件的值(一般而言,都是替换掉低优先级的值)
  • strict    严格的意思,就是不同的清单文件,相同属性的值应该一样,否则编译失败。(一般而言,就是不同的清单文件同一个属性其值要一样)
  • remove  删除某个属性。
  • removeAll 删除某个属性及其嵌套的子属性。(相当于remove,不过是有些时候删除很多子属性时,方便一些)
  • merge-only-attribute :Merge attributes in this tag only; do not merge nested elements 。只合并属性,不合并嵌套的属性
主工程app 的testActivity 声明:
 <activity android:name=".testActivity" tools:node="merge">
            <meta-data android:name="xxx" tools:node="replace"/>
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
 </activity>

module 的testActivity 声明:
 <activity android:name=".testActivity" >
            <meta-data android:name="xxx" tools:node="replace"/>
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
 </activity>

说明:嵌套的含义——针对上面的activity(属于module)来说,就是activity里面的attribute——mate ,intent-filter,现在activity的合并标记是 merge,假如这个activity 所在的module、主工程都声明了该activity,显然在最终的manifest只会有一份 声明,需要合并(所以说出现在 manifest 的任何属性都可以运用合并标记)。这时候,我们需要按照规则来合并。

应用场景:

remove:假如主工程中不能使用某个权限,比如说联系人权限,但你引用的jar包或者aar使用了该权限,这时候就可以通过remove 标记去掉该权限在不影响使用的情况下。

replace:引用的工程在某个属性使用了和主工程不同的值,这时候使用replace可以解决冲突;

 

属性标记

tools:remove = "attr, ..."  tools:replace="attr, ..."  tools:strict="attr, ..."

 

见明知意。也是替换,移除,严格,和node中差不多,那为什么又搞出一个属性标记呢?为啥不直接用node这一个节点标记呢?
官方文档给出的解释:

To instead apply a merge rule only to specific attributes in a manifest tag, use the following attributes. Each attribute accepts one or more attribute names (including the attribute namespace), separated by commas.

低优先级

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

而其他的一些属性只有一个确定的子属性,比如前面提到的 user-feature \ permission 等等,他们都是确定好哪个属性了,所以就用了node 。

<uses-permission android:name="android.permission.INTERNET" tools:node="merge"/> 
<uses-feature android:name="android.hardware.Sensor" tools:node="remove"/>

选择标记

应用场景:假如你只想对某个module 、library 实施一些规则,比如说,你引用了一个module, 一个aar ,你只想对aar包作用,这样你可以用selector 指明作用域(现在好像去掉了这个标记了,已经找不到了)

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

<uses-sdk>

场景1:假如依赖的jar 的minSdkVersion 大于主工程的值,这时候为了保持主工程的最小sdk值,可以使用 

<uses-sdk android:minSdkVersion="15" tools:overrideLibrary="要覆盖的包名" />

注意:现在manifest 中的minSDK 和targetSdk会被gradle覆盖,不推荐再写 ,所以这里只需要填写

<uses-sdk  tools:overrideLibrary="要覆盖的包名" />

使用IDE 的合并工具

合并策略 

合并工具通过寻找唯一的元素 (android:name ="") 或者 唯一的tag 比如<uses-sdk > ,按照如下总的规则去合并:

  1. 合并所有没有冲突的属性,若有冲突则按照冲突规则标记来;
  2. 只合并嵌套元素,比如activity 这种。
  3. 保持原来,并进行整和不同的属性值

下面看英文原文,上面的翻译不太准确:

 Merge policies

The manifest merger tool can logically match every XML element from one manifest file to a corresponding element in another file. The merger matches each element using a "match key": either a unique attribute value (such as android:name) or the natural uniqueness of the tag itself (for example, there can be only one <supports-screen> element). If two manifests have the same XML element, then the tool merges the two elements together using one of three merge policies:

Merge

Combine all non-conflicting attributes into the same tag and merge child elements according to their respective merging policy. If any attributes conflict with each other, merge them together with the merge rule markers.

Merge children only

Do not combine or merge the attributes (keep only the attributes provided by the highest priority manifest file) and merge child elements according to their merging policy.

Keep

Leave the element "as is" and add it to the common parent element in the merged file. This is used only when it is acceptable for there to be several declarations of the same element.

 Manifest element merge policies and match keys

Element Merge policy Match key
<action> Merge android:name attribute
<activity> Merge android:name attribute
<application> Merge There is only one per <manifest>
<category> Merge android:name attribute
<data> Merge There is only one per <intent-filter>
<grant-uri-permission> Merge There is only one per <provider>
<instrumentation> Merge android:name attribute
<intent-filter> Keep No matching; several declarations within the parent element are allowed
<manifest> Merge children only There is only one per file
<meta-data> Merge android:name attribute
<path-permission> Merge There is only one per <provider>
<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 android:screenSize attribute
<service> Merge android:name attribute
<supports-gl-texture> Merge android:name attribute
<supports-screen> Merge There is only one per <manifest>
<uses-configuration> Merge There is only one per <manifest>
<uses-feature> Merge android:name attribute (if not present, then the android:glEsVersion attribute)
<uses-library> Merge android:name attribute
<uses-permission> Merge android:name attribute
<uses-sdk> Merge There is only one per <manifest>
Custom elements Merge No matching; these are unknown to the merger tool so they are always included in the merged manifest
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章