文章目录

  • 0. 前言
  • 1. 优先级定义
    • 1.1 构建变体清单文件
    • 1.2 应用主模块清单文件
    • 1.3 库模块及依赖库清单文件
  • 2. 隐式系统权限
  • 3. 合并规则
  • 4. 属性标记
  • 5. 节点标记
  • 6. 常见问题

0. 前言

AndroidStuido 采用模块化构建工程的方式,每个模块配置一个 AndroidManifest.xml ,甚至每个构建类型、产品特性都可以配置一个 AndroidManifest.xml。最终生成 apk 的时候,按照下图指定的优先级进行合并处理。(图片来源:Google 官方文档)

1. 优先级定义

上图介绍了三种清单文件的合并流程,从左到右,优先级由低到高。

1.1 构建变体清单文件

构建变体清单,主要包含两种:buildType 和 productFlavor 。

优先级由高到低依次是:

  1. 产品特性(productFlavors)。
  2. 构建类型 (buildType)。
  3. 编译变体(前两者组合)。
    buildTypes {// 测试版本debug {}// 发行版本release {}}flavorDimensions 'stage', 'api'productFlavors {// 开发阶段dev {dimension 'stage'}// 生产阶段pro {dimension 'stage'}minApi21 {dimension 'api'}minApi23 {dimension 'api'}minApi26 {dimension 'api'}}

分别建立对应的编译变体目录和 AndroidManifest.xml 文件。

以上面的配置为例,优先级依次是(高到低的顺序):

dev -> minApi23 -> devMinApi23 -> debug -> devMinApi23Debug

  • dev 高于 minApi23 是因为 flavorDimensions 声明 stage 优先于 api 。
  • 不存在 devDebug 和 minApi23Debug 这样的组合。

1.2 应用主模块清单文件

主模块中的清单文件,即 main 目录下的 AndroidManifest.xml 文件。

1.3 库模块及依赖库清单文件

依赖库指依赖本地的 aar 文件或依赖远程 maven 仓库的 aar 文件。它们通常都包含一个 AndroidManifest.xml 文件。

jar 类型的三方库,只有 class 文件,不在此讨论范围。

与主模块相对立,库模块也可以包含多个构建类型和产品特性。首先,它们先按照 构建变体清单文件 定义的优先级,合并出自身的一个 AndroidManifest.xml ,再作为库模块的清单文件与主模块的清单文件合并。

2. 隐式系统权限

早期 Android 版本中,应用可以自由访问的 API ,在新版本中受到系统权限的限制。为兼容这些应用,新版本中会允许这些应用在无权限的情况下访问这些受限的 API 。

WRITE_EXTERNAL_STORAGE 最早出现在 Android API 4 中。那么,当你应用的 targetSdkVersion 设置成小于 4 时,也可以在无权限的情况下访问 外部存储

在 AndroidManifest 合并中,如果优先级低的 Manifest 中 targetSdkVersion 小于 4,那么在 合并后的 AndroidManifest 中,会自动添加这些隐式权限。

下面两张图是合并前后,app/AndrodManifest.xmllibrary/AndroidManifest.xml 的对比。

发现 合并前 app/AndrodManifest.xml 中未申明权限,但是合并后多了三条权限记录。

优先级较低的清单声明 向合并后的的清单添加的权限
targetSdkVersion <= 3 WRITE_EXTERNAL_STORAGE
READ_EXTERNAL_STORAGE
READ_PHONE_STATE
targetSdkVersion <= 15
且使用 READ_CONTACTS
READ_CALL_LOG
targetSdkVersion <= 15
且使用 WRITE_CONTACTS
WRITE_CALL_LOG

隐式系统权限主要为兼容 Android 早起版本。在 9102 年及以后的开发中并不多见。因此,了解即可。

3. 合并规则

借用官方的一张图,介绍了在合并过程中,默认的一些合并方式。

以 dev 是高优先级,debug 是低优先级为例:

<!-- dev 的 Manifest-->
<activityandroid:name="com.flueky.lib.TestActivity"android:exported="false"android:windowSoftInputMode="adjustPan"><meta-dataandroid:name="dev_index"android:value="dev" />
</activity>
<!-- debug 的 Manifest-->
<activity android:name="com.flueky.lib.TestActivity"android:exported="false"android:screenOrientation="portrait"><meta-dataandroid:name="debug_index"android:value="debug" />
</activity>
  1. dev 没有值,debug 值 B,合并后值 B,android:screenOrientation=“portrait”
  2. dev 值 A,debug 没有值,合并后值 A,android:windowSoftInputMode=“adjustPan”
  3. dev 值 A,debug 值 A,合并后值 A,android:exported=“false”

因此最终合并结果如下:

<!-- 合并后devDebug 的 Manifest-->
<activity android:name="com.flueky.lib.TestActivity"android:screenOrientation="portrait"android:exported="false"android:windowSoftInputMode="adjustPan"><meta-dataandroid:name="dev_index"android:value="dev" /><meta-dataandroid:name="debug_index"android:value="debug" />
</activity>

上述提到的都属性合并,但是 meta-data 标签最终却也合并了。标签的合并,大家都熟悉的是 Activity 的合并。每个模块的 Activity 只需要在该模块的 AndroidManifst.xml 注册,不需要在主模块中再次注册,因为最终会将它们合并在一起。

标签合并使用匹配键作为依据,activity 使用 android:name 作为匹配件,meta-data 也是。匹配键的属性值不同,即可无冲突合并。

下表列出,manifest 全部子标签的匹配键,做解决合并冲突的参考。

标签 合并策略 匹配键
action 合并 android:name 属性
activity 合并 android:name 属性
application 合并 每个 manifest 只有一个
category 合并 android:name 属性
data 合并 每个intent-filter 只有一个
grant-uri-permission 合并 每个 provider 只有一个
instrumentation 合并 android:name 属性
intent-filter 保留 不匹配;允许父元素内的多个声明
manifest 仅合并子元素 每个文件只有一个
meta-data 合并 android:name 属性
path-permission 合并 每个 provider 只有一个
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 只有一个
uses-configuration 合并 每个 manifest 只有一个
uses-feature 合并 android:name 属性
(如果不存在,则使用 android:glEsVersion 属性)
uses-library 合并 android:name 属性
uses-permission 合并 android:name 属性
uses-sdk 合并 每个 manifest 只有一个
自定义元素 合并 不匹配;合并工具并不知晓这些元素,
因此它们始终包含在合并后的清单中

Android 中通过标记的方式,在合并过程中解决冲突。按照前面介绍的属性合并和标签合并,对应采用属性标记和节点标记(标签作为节点)的方式人为解决冲突。

需要使用命名空间 Android tools

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

4. 属性标记

属性标记有四个。默认标记、移除、替换、选择器。

标记 作用
tools:strict 默认标记,无实际意义,需要使用 replace、remove 解决冲突
tools:replace 指定替换的属性名字
tools:remove 指定移除的属性名字
tools:selector 选择指定模块进行合并,结合 replace、remove 使用

这些例子比较简单 ,借用下官方的示例代码。

  1. replace 使用示例。
<!-- 低优先级 -->
<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">
  1. remove 使用示例。
<!-- 低优先级 -->
<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">
  1. selector 表示,只针对指定来源的低优先级 AndroidManifest.xml 进行合并处理。
<!-- 来源于 com.example.lib1 的低优先级 -->
<activity android:name="com.example.ActivityOne"android:windowSoftInputMode="stateUnchanged">
<!-- 高优先级 -->
<activity android:name="com.example.ActivityOne"android:screenOrientation="portrait"tools:remove="android:windowSoftInputMode"tools:selector="com.example.lib1">
<!-- 合并后 -->
<activity android:name="com.example.ActivityOne"android:screenOrientation="portrait">

5. 节点标记

节点标记使用 tools:node

属性值 作用
merge 没有发生冲突时合并标签中所有属性和所有嵌套元素,即是默认方式
merge-only-attributes 未正确验证,估计已失效。
remove 合并后移除此标签
removeAll 同 remove 类似,但是移除全部此标签
replace 替换低优先级中相同的标签
strict 使用时,遇到不匹配的标签都导致合并失败,需要使用上述属性值解决

1. 下面的结果,即使默认方式合并后。

<!-- 合并后devDebug 的 Manifest-->
<activity android:name="com.flueky.lib.TestActivity"android:screenOrientation="portrait"android:exported="false"android:windowSoftInputMode="adjustPan"><meta-dataandroid:name="dev_index"android:value="dev" /><meta-dataandroid:name="debug_index"android:value="debug" />
</activity>
  1. merge-only-attributes 与资料说明的不太一样,未发现正确用途 。

  2. 使用 remove 后,移除匹配键相同的标签。

<!-- 低优先级 -->
<activityandroid:name="com.flueky.lib.TestActivity"android:exported="false"android:screenOrientation="portrait"><meta-dataandroid:name="debug_index"android:value="debug" />
</activity>
<!-- 高优先级 -->
<activity
<activityandroid:name="com.flueky.lib.TestActivity"android:windowSoftInputMode="adjustPan"><meta-dataandroid:name="debug_index"tools:node="remove" />
</activity>
<!-- 合并后 -->
<activityandroid:name="com.flueky.lib.TestActivity"android:exported="false"android:windowSoftInputMode="adjustPan"android:screenOrientation="portrait">
</activity>
  1. 使用 removeAll 后:
<!-- 低优先级 -->
<activityandroid:name="com.flueky.lib.TestActivity"android:exported="false"android:screenOrientation="portrait"><meta-dataandroid:name="debug_index"android:value="debug" /><meta-dataandroid:name="dev_index"android:value="dev" />
</activity>
<!-- 高优先级 -->
<activityandroid:name="com.flueky.lib.TestActivity"android:windowSoftInputMode="adjustPan"><meta-datatools:node="removeAll" />
</activity>
<!-- 合并后 -->
<activityandroid:name="com.flueky.lib.TestActivity"android:exported="false"android:windowSoftInputMode="adjustPan"android:screenOrientation="portrait">
</activity>

removeAll 移除全部相同标签。

  1. 使用 replace 后,结果比较直观。
<!-- 低优先级 -->
<activityandroid:name="com.flueky.lib.TestActivity"android:screenOrientation="portrait"><intent-filter><action android:name="android.intent.action.SEND" /><category android:name="android.intent.category.DEFAULT" /></intent-filter>
</activity>
<!-- 高优先级 -->
<activityandroid:name="com.flueky.lib.TestActivity"android:windowSoftInputMode="adjustPan"tools:node="replace"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.DEFAULT" /></intent-filter>
</activity>
<!-- 合并后 -->
<activityandroid:name="com.flueky.lib.TestActivity"android:windowSoftInputMode="adjustPan"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.DEFAULT" /></intent-filter>
</activity>

使用 replace 合并后,直接按照高优先级中的配置。

6. 常见问题

集成第三方库时,遇到过最多的问题是:库的最小 sdk 版本小于项目的最小 sdk 版本。此时集成第三方库时必定出现问题。

解决方法:

<!-- com.flueky.library 即是第三方库的 packageName -->
<uses-sdk tools:overrideLibrary="com.flueky.library" />

最后,偷偷的说下,AndroidStudio 3.5.2 版本支持直接看合并后的 AndroidManifest.xml 文件。

源码地址

觉得有用?那打赏一个呗。去打赏

个人主页已经更新 ,欢迎收藏https://flueky.github.io/。

AndroidManifest 合并冲突处理相关推荐

  1. 如何解决Git中的合并冲突

    如何解决Git中的合并冲突? #1楼 对于想要半手动解决合并冲突的Emacs用户: git diff --name-status --diff-filter=U 显示所有需要解决冲突的文件. 依次打开 ...

  2. 【Git】Git 分支管理 ( 解决分支合并冲突 | 本地处理文件冲突 )

    文章目录 一.本地处理文件冲突 一.本地处理文件冲突 在下面的 [Git]Git 分支管理 ( 解决分支合并冲突 | 创建并切换分支 git switch -c feature1 | 修改 featu ...

  3. git分支操作、分支合并冲突解决

    目录 git bash分支命令 git bash分支简单操作 git bash分支合并冲突修改 git bash分支命令 注意:切换分支的时候,本地.暂存区的文件依然在本地.暂存区.也就是只是切换本地 ...

  4. git 合并冲突_git分支管理的策略和冲突问题

    备注: 知识点 关于分支中的冲突 分支管理的策略 分支策略 备注: 本文参考于廖雪峰老师的博客Git教程.依照其博客进行学习和记录,感谢其无私分享,也欢迎各位查看原文. 知识点 git log --g ...

  5. IDEA中Git合并冲突

    1.美图 2.背景 IDEA中Git合并冲突 先commit本地修改的文件到本地repository pull源码,因为存在代码冲突,所以接下来会自动弹出merge融合窗口,如下图:

  6. Eclipse的Git插件Egit: merge合并冲突具体解决方法

    Eclipse的Git插件Egit: merge合并冲突具体解决方法 参考文章: (1)Eclipse的Git插件Egit: merge合并冲突具体解决方法 (2)https://www.cnblog ...

  7. Git flow(使用工具自动git flow+解决合并冲突)

    Git flow(使用工具自动git flow+解决合并冲突) 1. Git Flow常用分支 生产分支(master) Master分支只能从其他分支合并,不可直接修改‌,Master分支的Comm ...

  8. git合并冲突【you have not concluded your merge】

    a分支merge b分支,出现合并冲突,解决冲突后再次merge报错"you have not concluded your merge", 解决办法:先解决冲突,然后提交合并,然 ...

  9. AndroidStudio合并冲突失败,总在merging状态

    今天有个大版本分支合并,但是合并冲突失败了,总在merging状态.提示 "Couldn't check the working tree for unmerged files becaus ...

  10. git 申请合并冲突:rebase 解决合成一条再合并

    问题描述 申请合并时出现:Merge blocked: the source branch must be rebased onto the target branch. 要求 source bran ...

最新文章

  1. 星辰变鸿蒙武器,星辰变手游亲手打造极品光武 成就炼器宗师
  2. 警方:“外卖员因获差评杀人”为假消息 造谣者被刑拘
  3. ASP.NET杂谈-一切都从web.config说起(2)(ConfigSections详解-中)
  4. 统一建模语言UML轻松入门(3)――静态建模:类和对象
  5. step-by-step多文件WEB批量上传(swfupload)的完美解决方案
  6. 显卡RTX 3090运行pytorch报错CUDA error: no kernel image is available for execution on the device
  7. 唯美“光效”PNG免扣素材大集合,一眼爱上!
  8. 什么是Ultrabook
  9. 为什么哈希表的容量一定要是 2的整数次幂?
  10. IT界的悲哀--做互联网,就要跳出互联网
  11. 实对称矩阵的特征值求法_机器学习和线性代数 - 特征值和特征向量
  12. python从入门到精通资源库_[百度网盘]PYTHON从入门到精通全套资料 - 磁力点点
  13. 学习理论:理论联系实际--演绎归纳演绎
  14. linux无法登录重置密码问题
  15. 计算机一直重启故障原因可能是,Windows电脑一直自动重启的原因和解决方法
  16. 【javaScript】原生实现窗口拖动效果
  17. 新产品、新特性、新生态丨一文回顾openGauss峰会云和恩墨分论坛150分钟的精彩...
  18. 代号斗罗显示服务器暂未开放,代号:斗罗内测版
  19. OpenCV基于Python霍夫圆检测—基于梯度的霍夫圆检测
  20. python安装库备忘

热门文章

  1. 手把手教用matlab做无人驾驶(十四)--项目实践(MPCC)
  2. UCOSIII任务创建与删除
  3. HDU6386 Age of Moyu 最短路dijkstra+堆优化
  4. AI智能安防视频平台EasyCVR获取设备告警信息异常情况的原因排查与解决办法
  5. 剑桥计算机博士姓名,我的剑桥博士生活系列之一
  6. CTG-MBOSS简述
  7. C#Random()函数详解
  8. 按住ctrl键不能批量选_如何在不按住Ctrl键的情况下遵循Outlook 2013中的超链接
  9. mysql 三天 分组查询_3mysql第三天 查询的指令补充
  10. 预览相机——Camera2基本用法