Android APK打包构建与增量更新

APK编译构建流程

背景

APK编译构建流程是开发者编写的Java、资源等源文件,以及引用的第三方库,被打包到一起,生成最终APK的流程,这里面涉及到多个输入、输出文件和工具。

整体打包流程可以分为7步,如下图所示:

汇总表格如下:

步骤 作用 工具 输入 输出
1.资源处理 将资源文件打包为R.java和编译后的资源文件 AAPT 原始资源文件 R.java, resources.arsc, 编译后的资源
2.AIDL处理 根据AIDL文件生成Java文件 aidl .aidl文件 .java文件
3.Java编译 将.java编译为.class javac .java文件 编译后的.class文件
4.DEX合成 把第三方库和.class文件合成dex DX,D8,R8,Multidex 第三方库,.class文件 classes.dex
5.APK打包 组装APK apkbuilder 原始资源文件 未经编译的资源,已编译的资源.so库文件,dex文件
6.签名 使用私钥进行签名 Jarsigner APK 签名后的APK
7.对齐 文件对齐,提升在设备上性能 zipalign 签名APK 签名且对齐后的APK

1.资源处理

Android Asset Packaging Tool (AAPT) 用于处理资源文件,并生成R.java,为代码中使用资源能提供索引。同时也生成了编译后的资源文件。

写代码时会自动调用aapt.exe,以下是AAPT执行的主要操作:

  1. 检查AndroidManifest.xml
  2. 引用其它资源包,如系统android命名空间下的资源
  3. 收集资源文件并处理overlay
  4. 加载资源到资源表ResourceTable
  5. 编译values资源并添加到资源表
  6. bag资源分配id,在编译其它资源前,先要给bag资源(如attrs)分配id,以便其它资源引用
  7. 编译xml资源文件:对layoutanimsanimators等资源进行编译,扁平化为二进制文件
  8. 编译AndroidManifest文件,生成资源表
  9. 生成R.java文件
  10. 生成resources.arsc文件

2.AIDL处理

Android Interface Definition Language (AIDL)工具用于在不同进程之间提供开放式接口,它将.aidl文件转换为相应的.java文件。

3.Java编译

将所有.java代码(源文件、AIDL文件、R.java)编译为.class文件。

4.DEX合成

Dalvik Executable (DEX) 文件,由D8DX工具将.class文件、依赖的三方库转换得到,有以下主要职责:

  1. 从.class到.dex:因为Android虚拟机不能直接运行Java字节码,需要转换为Dalvik字节码,最新的工具是R8
  2. 字节码优化:删除死代码(用不到的class),减少dex文件大小并提升运行效率
  3. dex分包:超过65535的话,生成classes.dex, classes2.dex, classes3.dex等,工具是Multidex

5.APK打包

使用apkbuilder或者gradle工具将【未经编译/已编译的资源、.dex文件、so库】打包到一个未签名的APK文件中。

6.签名

未经签名的设备需要经过签名才能在设备上面运行,可使用debug或者release密钥,通过jarsigner工具进行签名。

签名的目的

  • 身份验证:保证应用来源的正当和合法
  • 更新和版本管理:通过签名的应用表示,保证应用更新时不被劫持

V1签名与V2签名

V1V2两种签名模式,从Android 7.0开始,默认使用V2签名,但是为了保证向后兼容,大部分开发者同时使用这两种签名方式。

这两种签名在范围、存储位置有所不同。

  • V1签名:即JAR签名,为APK中每一个文件单独签名
  • V2签名:即完整APK签名,为整个APK文件进行一次签名,更安全且验证效率更高

7.对齐

使用zipalign工具对已签名的APK进行优化,从原理上来讲就是通过格式化zip文件夹中二进制文件的序列,达到提升系统解析速度。zipalign使用了4字节的边界对齐方式来映射内存,是的运行时系统可以直接mmap APK,通过空间换时间的方式提高执行效率。

对齐会改变APK的内容,因此会破坏之前的签名。


增量更新与差分包

在开发下载功能时,尤其是应用商店类APP,增量更新是一个很重要的功能,可以有效减少下载的数据量,提升下载完成率,优化用户使用体验。

什么是增量更新

增量更新基于BSDiff算法,计算两个APK字节码的差异,首先在服务端生成差分patch包,经过下载后,客户端使用同样的BSDiff算法,把patch与老APK合并,生成新APK,进行安装。

  1. 服务器计算并生成差分包
  2. 客户端下载差分包
  3. 客户端合并老APK与差分包,生成新APK
  4. 客户端安装新APK

相比于下载完整的新APK,很明显,增量更新最大的优点是数据传输量更小,下载安装更快。

BSDiff算法详解

什么是BSDiff

BSDiff是一种可执行文件的二进制差异构建和应用修补工具。

BSDiff的核心思想

是尽可能多的利用old文件中已有内容,尽可能少的加入新的内容来构建new文件。BSDiff算法的提出,基于可执行文件更新前后二阶变动的两个重要规律:

  1. 没有被更新代码所影响的代码段,在变为可执行文件后,该区域的二进制内容变化是极为稀疏的,即仅仅有部分指针或寄存器变动,通常不会超过一两个字节
  2. 更新后的代码和数据会有很大的位置变动,但这种变动大多为整块的移动,即某一块位置中代码内的指针地址变动均会有相同的位移值

这两个规律可以推导出一个重要事实:相同源代码对应的两个代码块中,大部分内容字节差为0,而少部分需要更新的地址位移数据又存在大量相同的位移值,即源代码相同的代码块差异数据可以被高效压缩。

基于此思想,BSDiff算法使用如下步骤来进行生成差异更新包:

  1. 将旧文件二进制使用后缀排序或哈希算法形成一个字符串索引
  2. 使用该字符串索引对比新文件,生成差异文件(difference file)和新增文件(extra file)
  3. 将差异文件和新增文件及必要的索引控制信息压缩为差异更新包

差分过程共生成3个文件,分别是差异文件、更新文件和控制文件。,这三个文件加一起会比新文件略大,但其中控制文件和差异文件是高度结构化的,意味着其均可被高效压缩,所以可以使用类似bzip2等压缩工具将更新包总文件进行非常有效的压缩。

实战:增量更新APK

这部分请参考掘金 Android_增量更新(BSDiff)详解

对增量更新的思考

  • 在生产场景中,由于线上存在多个版本的APP,因此需要针对不同版本生成差分包
  • 如果版本变更过大,可能导致差分包大于new包,此时直接使用new包下载更新即可
  • 出于安全和成功率考虑,需要对old包、new包、patch分别进行md5校验

参考资料

相关推荐
贵州晓智信息科技3 小时前
如何优化求职简历从模板选择到面试准备
面试·职场和发展
百罹鸟3 小时前
【vue高频面试题—场景篇】:实现一个实时更新的倒计时组件,如何确保倒计时在页面切换时能够正常暂停和恢复?
vue.js·后端·面试
古木201912 小时前
前端面试宝典
前端·面试·职场和发展
码农爱java19 小时前
设计模式--抽象工厂模式【创建型模式】
java·设计模式·面试·抽象工厂模式·原理·23种设计模式·java 设计模式
Jiude19 小时前
算法题题解记录——双变量问题的 “枚举右,维护左”
python·算法·面试
长安05111 天前
面试经典题目:LeetCode134_加油站
c++·算法·面试
正在绘制中1 天前
Java重要面试名词整理(一):MySQL&JVM&Tomcat
java·开发语言·面试
沉默王二1 天前
虾皮开的很高,还有签字费。
后端·面试·github
Do1 天前
时间系列三:实现毫秒级倒计时
前端·javascript·面试
小蜗牛慢慢爬行1 天前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试