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
2
3
4
5
6
7
8
9
10
11
12
<activity android:name=".MergeActivity"
tools:node="remove"> // 合并时,删除此标签

</activity>

tools:selector="lib1" // 指定导入库使用合并规则(偏见选择器)

<permission android:name="permission1"
tools:node="remove" // 需先定义一个合并规则
tools:selector="lib1"> // 定义的规则只对此库生效
// 如果属性来自第优先级库"lib1"的时候,使用规则remove.
</permission>

属性标记:针对属性使用规则
tools:remove=”attr0,attr1,…”
在标签内移除指定属性,防止lib中引入非必要属性。
tools:replace=”attr,…”
在标签内,指定属性使用高优先级清单中的值。

依赖库属性冲突解决利器 tools-replace:当项目中引用了其他库时,若第三方库中的组件属性与本地库发生了冲突,可以通过 tools:replace 解决,被 tools:replace 声明的组件会覆盖第三方库中的属性:

示例:如组件化中,应用图标的设置,app 模块与其他模块的图标设置不一致,可在 app 模块中添加代码:

AndroidManifest.xml
1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<application
tools:replace="android:icon,android:roundIcon"
...
</application>
</manifest>

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
2
3
4
5
6
7
8
9
10
11
12
aaptOptions{
// -f,强行覆盖已存在文件。-s,resources文件。-A,assets文件
// -M,manifest文件。
// --auto-add-overlay,找不到相同值,则自动添加进去,以防值不同出错。
additionalParameters '-s',
'/Users/handsome/work/ResourceDemo/app/src/main/res2',
'-s',
'/Users/handsome/work/ResourceDemo/app/src/main/res1',
'--auto-add-overlay'
// 最终:res=res+overlay资源首次出现的新资源。
// 比如上例,则先寻找是否有res2.
}

简述多渠道打包及原理和常用操作?

针对每一个渠道(应用市场)都生成一个带有渠道标识的apk文件

原理:用户下载启动应用,获取渠道标识,和设备的唯一标识,并上传到服务器里面,服务器这里就 会根据获取的记录,根据渠道号然后判断是否存在该服务器的表里面.(打标记,取标记,上传标记)

1)友盟多渠道打包:在清单文件中定义一个占位符,在gradle脚本中替换占位符(会使用到Python)

2)美团打包,在meta-data中创建一个空的文件,以文件名标识渠道,做一个解压与压缩的操作,速度会比较快

3)新一代多渠道打包,将渠道标识添加到.apk文件的末尾,并不会对源文件损坏