❤️Android Apk 的打包过程 ❤️ 只需两幅图Android apk 包体积优化
官方介绍
在分析安装过程之前,需要先了解一下 Android 项目是如何经过编译->打包生成最终的 .apk 格式的安装包。谷歌有一张官方图片来描述 apk 的打包流程,如下图所示。
Android 应用模块的构建过程(如上图所示)遵循以下一般步骤:
-
1、编译器将你的源代码转换为 DEX(Dalvik 可执行文件)文件,其中包括在 Android 设备上运行的字节码,以及其他所有内容到编译资源中。
-
2、APKPackager将DEX文件和编译后的资源组合成一个APK。但是,在将你的应用安装并部署到Android设备之前,必须对APK进行签名。
- 3、APKPackager使用调试或发布密钥库对你的 APK 进行签名:
-
3.1如果你正在构建应用程序的调试版本,即你打算仅用于测试和分析的应用程序,则打包程序会使用调试密钥库对你的应用程序进行签名。Android Studio 使用调试密钥库自动配置新项目。
-
3.2如果你正在构建你打算在外部发布的应用程序的发布版本,则打包程序会使用发布密钥库对你的应用程序进行签名。
-
-
4、在生成最终的 APK 之前,打包程序使用zipalign工具来优化你的应用程序,以便在设备上运行时使用更少的内存。
在构建过程结束时,你将拥有应用的调试 APK 或发布 APK,可用于部署、测试或发布给外部用户。
以上是官方介绍。下面咱开始自己的理解。
自我救赎
开始新项目时,Android Studio 会自动为您创建其中的部分文件,并为其填充合理的默认值。所以不管一个完整的 Android 项目可能包含多个 module,而从宏观上看每一个 module 中的内容可以分为 2 部分:
-
Resources 资源文件
-
Java 或者 Kotlin 源代码。
因此整个项目的编译打包过程也是针对这 2 部分来完成,如下图:
编译阶段
Resources 资源文件
资源文件包括项目中 res 目录下的各种 XML 文件、动画、drawable 图片、音视频等。AAPT 工具负责编译项目中的这些资源文件,所有资源文件会被编译处理,XML 文件(drawable 图片除外)会被编译成二进制文件,所以解压 apk 之后无法直接打开 XML 文件。但是 assets 和 raw 目录下的资源并不会被编译,会被原封不动的打包到 apk 压缩包中。
资源文件编译之后的产物包括两部分:resources.arsc 文件和一个 R 文件。前者保存的是一个资源索引表,后者定义了各个资源 ID 常量。这两者结合就可以在代码中找到对应的资源引用。如 下图 文件:
可以看出,R 文件 中的资源 ID 是一个 4 字节的无符号整数,用 16 进制表示。其中,最高的 1 字节表示 Package ID,次高的 1 个字节表示 Type ID,最低的 2 字节表示 Entry ID。
resources.arsc 相当于一个资源索引表,也可以理解为一个 map 映射表。其中 map 的 key 就是 R.java 中的资源 ID,而 value 就是其对应的资源所在路径。实际上 resources.arsc 里面还有其他信息,关于 resource.arsc 的解析可以参考 解析编译之后的Resource.arsc文件格式。
源码部分
项目中的源代码首先会通过 javac 编译为 .class 字节码文件,然后这些 .class 文件连同依赖的三方库中的 .class 文件一同被 dx 工具优化为 .dex 文件。如果有分包,那么也可能会生成多个 .dex 文件。
实际上源代码文件也包括 AIDL 接口文件编译之后生成的 .java 文件,Android 项目中如果包含 .aidl 接口文件,这些 .aidl 文件会被编译成 .java 文件。
打包阶段
最后使用工具 APK Builder 将经过编译之后的 resource 和 .dex 文件一起打包到 apk 中,实际上被打包到 apk 中的还有一些其他资源,比如 AndroidManifest.xml 清单文件和三方库中使用的动态库 .so 文件。
APK文件结构
APK(Android Package),可以看做是一个zip压缩包,可以将.apk改为.zip解压,其文件结构如下:
-
assert:存放的原生资源文件,通过AssetManager类访问
-
lib:native库文件
- META-INF:存放签名信息,用来保证APK包的完整性和系统的安全。系统安装APK时,应用管理器会按照对应算法对包里文件做校验,如果校验结果与META-INF中内容不一致,则不会安装这个APK。
-
CERT.SF:生成每个文件相对的密钥
-
MANIFEST.MF:数字签名信息
-
-
res:种资源文件系统会在R.java里面自动生成该资源文件的ID,所以访问这种资源文件比较简单,通过R.XXX.ID即可
-
AndroidManifest.xml:每个应用都必须定义和包含,描述应用的名字、版本权限、引用的库文件等信息。apk中的AndroidManifest.xml经过压缩,可以通过AXMLPrinter2工具解开。
-
classes.dex:是JAVA源码编译后生成的JAVA字节码文件。但Android使用的dalvik虚拟机与标准的JAVA虚拟机不兼容,dex文件与class文件相比,不论是文件结构还是opcode都不一样。
-
resources.arsc:编译后的二进制资源文件。 apk 创建好之后,还不能直接使用。需要使用工具 jarsigner 对其进行签名,因为 Android 系统不会安装没有进行签名的程序。签名之后会生成 META_INF 文件夹,此文件夹中保存着跟签名相关的各个文件。
PackageManagerService(PMS) 在安装过程中会检查 apk 中的签名证书的合法性,具体内容稍后介绍。
常理来说,签名之后的 apk 应该是可以正常安装使用了,但是实际打包过程还会多一步使用工具 zipalign 对 apk 优化操作。
zipalign:是一种 zip 归档文件对齐工具。它对 apk 中的未压缩资源(图片、视频等)进行对齐操作,相对于文件开头都是对齐的。这样一来,你便可直接通过 mmap(2) 访问这些文件,而无需在 RAM 中复制相关数据并减少了应用的内存用量。
在将 APK 文件分发给最终用户之前,应该先使用 zipalign 进行优化。如果你使用 Android Studio 进行构建,则此步骤会自动完成。
mmap(2):mmap, munmap - 将文件或设备映射或取消映射到内存中。
至此一个完整的 apk 安装包就创建成功。
整个编译打包流程可以用下图来描述:
AIDL:AIDL 是 Android 中 IPC(Inter-Process Communication,进程间通信)方式中的一种,AIDL是Android Interface definition language的缩写,对于小白来说,AIDL的作用是让让你可以在自己的 APP 里绑定一个其他 APP 的Service,这样你的 APP 可以和其他 APP 交互。
注意:只有在需要不同应用的客户端通过 IPC 方式访问服务,并且希望在服务中进行多线程处理时,你才有必要使用 AIDL。如果你无需跨不同应用执行并发 IPC,则应通过实现 Binder 来创建接口;或者,如果你想执行 IPC,但不需要处理多线程,请使用 Messenger 来实现接口。 无论如何,在实现 AIDL 之前,请你务必理解绑定服务(绑定服务是 Service 类的实现,可让其他应用与其进行绑定和交互。)。
AAPT2:会解析资源、为资源编制索引,并将资源编译为针对 Android 平台进行过优化的二进制格式。
javac:将所有 .java 文件 (包括 R 文件和 aidl 生成的 .java 文件),通过 javac 工具生成 .class 文件。
dx: .class 文件连同依赖的三方库中的 .class 文件一同被 dx 工具优化为 .dex 文件。
Apk 已经打包好了,接下来看一下 PackageManagerService 是如何将其安装到设备中的这个下文给到。
相关推荐
Android Gradle 详解
Android apk 包体积优化
文章来源: shuaici.blog.csdn.net,作者:帅次,版权归原作者所有,如需转载,请联系作者。
原文链接:shuaici.blog.csdn.net/article/details/120343896
- 点赞
- 收藏
- 关注作者
评论(0)