Manifest合并

Stella981
• 阅读 813

通过Android Studio和基于Gradle的构建,每个app都可以在多个位置包含manifest文件,比如productFlavor的src/main/文件夹,libraries,Android Library工程的Android ARchive (AAR)和dependencies。在构建过程中,manifest合并过程会把你的app所包含的多个AndroidManifest.xml文件中的设定结合起来,为app的打包和发布产生一个单独的APK manifest文件。Manifest设定会基于manifest的优先级来合并,优先级则由manifest的文件位置决定。构建你的app会把特定build variant的这些manifests的manifest元素,属性和子元素都合并起来。

合并冲突规则

当要合并的manifests包含有相同的manifest元素且具有不同的属性值,而又不能基于默认的合并冲突规则解析时会发生合并冲突。Conflict markers and selectors也可以定义定制的合并规则,比如允许一个导入的库具有比其它更高优先级的manifests中定义的版本更高的minSdkVersion。

manifest合并优先级决定了在合并冲突中保留那个manifest的设定,具有更高优先级的manifest中的设定将覆写更低优先级的manifest中的设定。

下面的列表详细说明了在合并过程中,哪个manifest设定具有更高的优先级:

  • 最高优先级:buildType manifest设定
  • 高优先级:productFlavor manifest设定
  • 中等优先级:app工程的src/main/目录下的Manifests
  • 低优先级:依赖及库的manifest设定

Manifest合并冲突基于下面的规则在XML节点和属性的层次进行解决:

High Priority Element

Low Priority Element

Manifest Merge Result

no attribute

no attribute

no attribute

attribute set to default

default attribute

attribute set to non-default

low priority attribute

attribute set to default

no attribute

default attribute

attribute set to non-default

high priority attribute

attribute set to default

attribute set to default

default attribute

attribute set to default

attribute set to non-default

low priority attribute

attribute set to non-default

attribute set to default

high priority attribute

attribute set to non-default

attribute set to non-default

Merge if settings match, otherwise causes conflict error.

manifest合并规则的例外:

uses-feature android:required; 和uses-library android:required元素默认值为true,构建系统用_OR_来合并这些元素,以便于任何需要的feature或library都可以被包含在产生的APK中。

如果没有声明元素,minSdkVersion和targetSdkVersion的默认值为1。当出现合并冲突时,将使用更高优先级的manifest中的那个。

导入一个minSdkVersion值比app的src/main/ manifest的对应值更高的library将产生一个error,除非使用了overrideLibrary冲突marker。

注意:如果没有显式地声明,targetSdkVersion默认值为minSdkVersion。如果在所有的manifest或build.gradle文件中都没有minSdkVersion元素,则其值为1。

当导入了一个其targetSdkVersion值比app的src/main/ manifest的值低的library时,manifest合并过程显式地(explicitly)获取权限并确保导入的library能正常工作。

manifest元素只合并子manifest元素。

intent-filter元素总是原封不动地被添加到合并之后的manifest里共同的父节点下。

重要:合并了manifests之后,构建过程将用build.gradle文件中也出现的设定覆盖最终的manifest设定。更多细节请参考Configuring Gradle Builds

合并冲突标签(Markers)和选择器(Selectors)

Manifest标签和选择符通过特定的冲突决议规则覆盖默认的规则。比如,使用一个冲突标签来将一个具有更高minSdkVersion值的library manifest与更高优先级的manifest合并,或者合并具有相同activity但它们具有不同android:theme值的manifests。

合并冲突标签(Markers)

一个合并冲突标签(Markers)是Android tools命名空间的一个特殊属性,它定义了一个特别的合并冲突决议规则。创建一个冲突标签来为默认合并规则无法处理的合并冲突避开一个合并冲突error。

merge

  • 根据合并规则进行合并时没有冲突就合并属性。这是默认的合并行为。

replace

  • 用高优先级manifest中的属性值替换低优先级manifest的属性值。

strict

  • 设置合并策略的层次,以便于在 合并后的元素具有相同的属性但值不同时 产生一个构建失败,除非通过冲突规则解决了冲突。

merge-only

  • 只合并低优先级的属性。

remove

  • 从合并后的manifest中移除特定的低优先级元素。

remove-All

  • 从合并后的manifest中移除所有相同节点类型的低优先级元素。

默认情况下,manifest合并过程为节点应用merge合并标签。所有声明的manifest属性默认为strict合并策略。

要设置一个合并冲突标签,首先要在AndroidManifest.xml文件中声明命名空间。然后,在manifest中键入合并冲突标签来描述一个定制的合并冲突行为。这个例子插入了replace标签设置一个replace行为来解决android:icon和android:label manifest元素间的冲突。

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

标签属性

冲突标签使用tools:node和tools:attr属性来把合并行为限制在特定的XML节点或属性之上。

tools:attr属性只使用restrict,remove,和replace合并行为。一个特定的元素可以应用多个tools:attr标签值,比如使用tools:replace="icon, label, theme"替换低优先级的icon,label,和theme属性。

导入的库的合并冲突标签

overrideLibrary冲突标签应用于manifest声明,它被用于导入一个库即使那个库的值,比如minSdkVersion,被设置为与其它高优先级manifests中的的值不同。

没有这个标签的话,来自于值的库manifest合并冲突将导致合并过程失败。

这个例子使用了overrideLibrary冲突标签来解决src/main/ manifest中的minSdkVersion值与一个导入的库的manifest之间的合并冲突。

src/main/ manifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.android.example.app"
   xmlns:tools="http://schemas.android.com/tools">
   ...
   <uses-sdk android:targetSdkVersion="22" android:minSdkVersion="2"
             tools:overrideLibrary="com.example.lib1, com.example.lib2"/>
   ...

库的manifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.example.lib1">
     ...
         <uses-sdk android:minSdkVersion="4" />
     ...
    </manifest>

注意:默认的合并过程不允许导入具有比app的src/main/ manifest中所定义的minSdkVersion值更高的库,除非使用了overrideLibrary冲突标签。

标签选择器(Marker Selectors)

标签选择器将一个合并行为限制在一个特定的低优先级manifest。比如,一个标签选择器可以被用于只从一个库中移除一个权限,同时允许其它的库请求相同的权限。

这个例子使用了tools:node标签来移除permisionOne属性,同时tools:selector选择器指定了特定的库_com.example.lib1_。permisionOne权限只被从lib1库的manifest中过滤掉。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.android.example.app"
   xmlns:tools="http://schemas.android.com/tools">
   ...
   <permission
         android:name="permissionOne"
         tools:node="remove"
         tools:selector="com.example.lib1">
   ...

向一个Manifest中注入Build Values

Manifest合并也可以被配置为使用manifest占位符,来向manifest属性里插入build.gradle文件中的属性值。

Manifest占位符使用${name}语法来插入属性值,其中name是被插入的build.gradle属性。build.gradle文件使用manifestPlaceholders属性来定义占位符的值。

注意:apps中的未解决占位符名称将导致构建失败。库中的未解决占位符名称产生warnings,并需要在库被导入到一个app中时被解决。

这个例子展示了manifest占位符${applicationId}被用于把build.gradle的applicationId属性值注入到android:name属性值。

注意:Android Studio为 构建文件中没有显示的build.gradle applicationId值提供了一个默认的${applicationId}占位符。当把库模块构建为一个AAR(Android ARchive)包时,不要在manifest合并设定中提供一个自动的@{applicationId}占位符。而要使用一个不同的占位符,比如@{libApplicationId},如果你想要在打包的库中包含应用程序Ids的话为它提供一个值。

Manifest项(entry):

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

Gradle构建文件:

android {
   compileSdkVersion 22
   buildToolsVersion "22.0.1"

   productFlavors {
       flavor1 {
           applicationId = "com.mycompany.myapplication.productFlavor1"
       }
}

合并后的manifest值:

<action android:name="com.mycompany.myapplication.productFlavor1.foo">

manifest占位符语法和构建文件的manifestPlaceholders属性可被用于注入其它的manifest值。对于applicationId之外的其它属性,manifestPlaceholders属性可以被显式地声明在build.gradle文件中。这个例子展示了manifest占位符注入activityLabel值。

Gradle构建文件:

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

manifest文件中的占位符:

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

注意:占位符值支持部分值注入,比如android:authority="com.acme.${localApplicationId}.foo"。

跨Product Flavor组的Manifest合并

当使用了GroupableProductFlavor属性时,product flavor组中的任何manifests的manifest合并优先级将遵循构建文件(build file)中product flavor组列出的顺序。manifest合并过程将基于配置的build variant为product flavor创建一个单独的合并manifest。

比如,如果一个variant分别引用了product flavor组ABI,Density,API和Prod的product flavors x86,mdpi,21和paid,它们在build.gradle文件中也以这样的顺序列出,则manifest合并过程以这个优先级顺序合并manifests,即遵循product flavors在构建文件中列出的顺序。

为了描绘这个例子,下面的表显示了每个product flavor组的product flavors是如何列出的。这个product flavors和组的结合定义了build variant。

Product Flavor Group

Product Flavor

ABI

x86

density

mdpi

API

22

prod

paid

Manifest合并顺序:

  • prod-paid AndroidManifest.xml(最低优先级)被合并进API-22 AndroidManifest.xml中。
  • API-22 AndroidManifest.xml被合并进density-mpi AndroidManifest.xml中。
  • density-mpi AndroidManifest.xml被合并进ABI-x86 AndroidManifest.xml(最高优先级)中。

隐式权限(Implicit Permissions)

导入一个其目标Android runtime隐蔽地获取权限的库可能自动地向最终的合并manifest中添加权限。比如,如果一个targetSdkVersion为16的应用程序导入了一个targetSdkVersion为2的库,Android Studio将添加WRITE_EXTERNAL_STORAGE权限以确保权限可以在SDK版本间兼容。

注意:更近一些的Android版本把隐式的权限替换为了权限声明。

Importing this library version

Declares this permission in the manifest

targetSdkVersion < 2

WRITE_EXTERNAL_STORAGE

targetSdkVersion < 4

WRITE_EXTERNAL_STORAGE, READ_PHONE_STATE

Declared WRITE_EXTERNAL_STORAGE

READ_EXTERNAL_STORAGE

targetSdkVersion < 16 and using the READ_CONTACTS permission

READ_CALL_LOG

targetSdkVersion < 16 and using the WRITE_CONTACTS permission

WRITE_CALL_LOG

处理Manifes合并Build Errors

在构建过程中,manifest合并过程在模块的build/outputs/logs文件夹下的manifest-merger--report.txt文件中存储了每一个合并事务的记录。模块的每次build variants产生一个不同的log文件。

当发生了一个manifest合并build erros时,合并过程在log文件中记录了错误消息,该消息描述了合并冲突。比如,下面的manifests之间的android:screenOrientation合并冲突导致了一个build error。

高优先级manifest的声明为:

<activity
   android:name="com.foo.bar.ActivityOne"
   android:screenOrientation="portrait"
   android:theme="@theme1"/>

低优先级manifest的声明为:

<activity
   android:name="com.foo.bar.ActivityOne"
   android:screenOrientation="landscape"/>

错误log为:

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

Done。

原文

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
P2P技术揭秘.P2P网络技术原理与典型系统开发
Modular.Java(2009.06)\.Craig.Walls.文字版.pdf:http://www.t00y.com/file/59501950(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.t00y.com%2Ffile%2F59501950)\More.E
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这