Android-打包过程
模块优先级
模块优先级:
- 从根项目出发,优先级依次降低 。
- 优先级和依赖顺序一致。(APP>A>B)
- 深度遍历法则。(APP>A>C>D>B)
- 重复依赖,取最后一次的优先级。(APP>A>D>B>C)
图例:
打包时做了什么
图例:
Android 中,限制整个应用的方法数不能超过 65536,解决的方法有两种:
- Google 提供的 multidex 方案:
通过将一个 dex 文件拆分为多个 dex 文件来避免单个 dex 文件方法数越界。 - 动态加载(插件化技术):
可直接加载一个 dex 形式的文件,将部分代码打包到一个单独的 dex 文件中(也可以是 dex 格式的 jar 或者 apk),并在程序运行时根据需要去动态加载 dex 中的类。
它的优势在于除了解决缓解方法数越界的问题,还可为程序提供按需加载的特性,同时还为应用按模块更新提供了可能性。
资源文件线路的处理过程
图例:
Assets文件合并规则
合并规则:优先级高覆盖优先级低
图例:
Q1:本地有 assets 文件,打包 apk 中却找不到。
A1:新建 Assets 文件夹姿势不对。(以防无法识别,正确方法创建assets文件夹应是:New -> Folder -> Assets Folder)
A2:Gradle 文件中 aaptOptions 对 Assets 做限制。
Manifest清单合并
Lowest priority - - - - - - - - - - - - - - - - - >Highest priority
AndroidManifest.xml(library manifest)–(merge)–>
AndroidManifest.xml(main manifest)–>
AndroidManifest.xml(partial merge)–(merge)–>
AndroidManifest.xml(build variant manifest)–>
AndroidManifest.xml(final)
Manifest清单合并:
- 清单优先级&合并顺序:遵循整体优先级(高的合并低的)
- 标签不同,合并规则不同。
默认合并规则:
高优先级属性 | 低优先级属性 | 属性的合并结果 |
---|---|---|
没有值 | 没有值 | 没有值(使用默认值) |
没有值 | 值B | 值B |
值A | 没有值 | 值A |
值A | 值A | 值A |
值A | 值B | 冲突错误,必须添加一个合并规则标记 |
特殊标签:
a:manifest 仅使用高优先级的属性(manifest本身,只使用创建打包apk的manifest,其它不要。)
b:uses-library,uses-feature [require] or 合并(类似//,真假则为真。)
c:uses-adk minSdkVersion 使用较高值,targetSdkVersion 使用高优先级,并添加相应权限。
d:intent-filter 不匹配,直接合并。
Manifest合并冲突
xmlns:tools=”http://schemas.android.com/tools"
tools:Node节点标记(作用域为整个节点)
tools:selector选择器(当依赖库与自己的manifest冲突了,可以借此做处理。)
tools属性标记:replace,remove,strict(针对属性)
类似如果项目发布后,提示不支持最新版本,则有可能是项目依赖包定义的max版本太低,因为一般不定义max版本,则默认为向上无限兼容,一直支持最新版本,但依赖包定义了,则会使用依赖包的max版本。
tools:node=”merge/merge-only-attributes/remove/removeAll/strict”
.merge:若没有冲突,则合并标签内的所有属性以及所有嵌套元素。
.merge-only-attributes:仅合并此标签内的属性,不合并嵌套元素。
.remove:删除此标签。
.removeAll:删除同一级别的相似标签。
.replace:替换低优先级清单内标签。
.strict:清单不匹配时会导致构建失败。
示例:
1 | <activity android:name=".MergeActivity" |
属性标记:针对属性使用规则
tools:remove=”attr0,attr1,…”
在标签内移除指定属性,防止lib中引入非必要属性。
tools:replace=”attr,…”
在标签内,指定属性使用高优先级清单中的值。
依赖库属性冲突解决利器 tools-replace:当项目中引用了其他库时,若第三方库中的组件属性与本地库发生了冲突,可以通过 tools:replace
解决,被 tools:replace
声明的组件会覆盖第三方库中的属性:
示例:如组件化中,应用图标的设置,app 模块与其他模块的图标设置不一致,可在 app 模块中添加代码:
1 |
|
tools:strict=”attr,…”
指定属性如果不匹配则编译失败
注:在左下角Text旁边的选项,可以通过不同颜色查看是哪个依赖包依赖进来的。
XML资源
默认合并策略:高优先级覆盖低优先级。
appt 分配 ID,放入 resource.arsc 中。
libB strings.xml(key->B,key2->B1) – libA strings.xml(key->A) –> main –>
APK(key->A(keyA覆盖了keyB),key2->B1(key2没有冲突))
当模块化时,会将定义的统一放在base库里面,但这样会导致优先级比较低,(因为大家都会引用,而优先级会使用最后一次的,也就是前面所说的重复依赖),这样当其它依赖库定义了相同文件时,则因为优先级的关系会覆盖掉,也就导致了类似于名称变成了demo,或者图标变成了绿机器人这些问题。
所以提倡,资源文件,谁用就放在哪里,放在它的module里面。
如果一定要有公共模块,则可以重命名,比如 icon 图标,可以在前加个名字。
防止资源覆盖:
未雨绸缪,最好在项目开始时,就做划分,做好规划。
- 模块化命名:module_XX
- 重要资源,提升优先级。
- 使用tools防止冲突和覆盖。
Aapt overlay
假设冲突了,比如因为需要换个图标,或者换肤,一般是修改后,打个包,再换回来,但太麻烦,则可以使用 Aapt overlay,也就是添加指定重叠包。
比如在 main 下添加重叠包:main/java,res,res1,res2
并且在build.gradle中添加:
1 | aaptOptions{ |
简述多渠道打包及原理和常用操作?
针对每一个渠道(应用市场)都生成一个带有渠道标识的apk文件
原理:用户下载启动应用,获取渠道标识,和设备的唯一标识,并上传到服务器里面,服务器这里就 会根据获取的记录,根据渠道号然后判断是否存在该服务器的表里面.(打标记,取标记,上传标记)
1)友盟多渠道打包:在清单文件中定义一个占位符,在gradle脚本中替换占位符(会使用到Python)
2)美团打包,在meta-data中创建一个空的文件,以文件名标识渠道,做一个解压与压缩的操作,速度会比较快
3)新一代多渠道打包,将渠道标识添加到.apk文件的末尾,并不会对源文件损坏