Android构建会经历资源合并打包、源码编译、dex生成及打包签名等步骤。
本文对资源合并打包进行一下小的总结
资源合并
上面一图总结了在构建过程中的资源合并情况。
- 资源合并包括本地与第三方库的
assets目录
、res目录
、Androidmanifest.xml
- 对于
assets目录
、res目录
(res/values文件夹除外
)的资源合并一旦发生冲突,会优先使用本地资源。 - 对于
res/values文件夹
,会进行内容上的合并,后合并的会覆盖有冲突的前面资源,而且合并先后顺序无法确定。 - 对于
Androidmanifest.xml
,我们的apk最终只会包含一个AndroidManifest.xml
,但是因为我们的main source set
,build variants
和引入的第三方依赖都可能存在Manifest
文件,这时候就需要做合并。如果一个低优先级的xml结点不能匹配任何高优先级的结点就会被加入到高优先级的Manifest文件里,如果匹配上了则会进行合并,如果该结点下的存在相同属性在不同文件里具有不同的值时则会报错需要在较高优先级的manifest文件里添加合并规则标识。(Androidmanifest.xml的优先级:buildType
设置 >productFlavor
设置 >src/main
>dependency&library
) - 合并规则标识
- merge:默认合并操作。
- remove:移除指定的低优先级的属性
- remove-All:移除相同节点类型下所有低优先级的属性
- replace:高优先级替换低优先级
Manifest
文件中的属性
- 另外,manifest在对文件进行合并后,还会根据build.gradle的设置覆盖相关属性。
举个例子,下面代码进行合并,最后的 label 是 app_name;allowBackup 是 true。
<!--src/main的Androidmanifest.xml-->
<application
android:name="MyApplication"
android:icon="@drawable/ic_launcher"
android:allowBackup="true"
android:label="@string/app_name"
<!-- 合并规则标识 -->
tools:replace="android:allowBackup,android:label">
</application>
<!--dependency&library的Androidmanifest.xml-->
<application
android:name="MyApplication"
android:icon="@drawable/ic_launcher"
android:allowBackup="false"
android:label="@string/app_name222">
</application>
下面代码向Manifest文件注入build变量值
<!--Gradle文件-->
android {
productFlavors {
free {
<!--manifestPlaceholders 相当于占位符的意思,会替换覆盖Manifest文件对应的属性-->
manifestPlaceholders = [ activityLabel:"freeName"]
}
pro {
manifestPlaceholders = [ activityLabel:"proName" ]
}
}
}
<!--Manifest文件-->
<activity android:name=".MainActivity" android:label="${activityLabel}" >
AAPT打包
通过AAPT
(Android Asset Packaging Tool
)处理后,会输出2个文件:
- 一个为为app.ap,实际上为一个压缩包,包含了
assets
、res
、Androidmanifest.xml
与resources.arsc
。 - 一个
R.java
,为项目各资源分配了不同的id,id为4字节无符号(8位)整数,最高字节表示package id
,次高字节表示type id
,后2字节表示资源在当前类型中出现的序号,如R.string.appname=0x7f07006b中的0x7f代表当前正在编译的资源包,0x07代表string类型,0x006b代表app_name在string类型中出现的序号。(这里package id为插件化的资源合并提供了可操作的地方)
assets是不需要做任何处理的,res/raw只需分配id后与assets一起直接打包到应用程序中;基于下述原因,其它xml文件则会被编译成二进制。
- 编译过程中,会把xml中的所有的字符串进行收集去重,形成字符串资源池,元素中用到字符串的地方将被替换成相应的索引,进一步减少文件大小。(字符串变成了整数值的索引,相同字符串引用同一个索引)
- 二进制格式的xml把标签属性/値转换为相应的索引后,避免了字符串解析,从而提高了解析速度。
资源索引表resources.arsc
记录了从资源id到文件路径的转换关系,当应用通过Resources
类获取res
文件资源时,会先从resources.arsc
中拿到文件路径,然后通过AssetManager
进行访问。
- Android资源管理框架实际就是由AssetManager和Resources两个类来实现的。
- Resources类可以根据ID来查找资源。
- AssetManager类根据文件名来查找资源。
再补上一张官方图总结总结应用程序资源的编译、打包以及查找过程
参考文章: