Android APK组成&编译打包流程详解

Android APK(Android Package)是 Android 应用的安装包文件,其组成和打包流程涉及多个步骤和文件结构。以下是详细的说明:


一、APK 的组成

APK 是一个 ZIP 格式的压缩包,包含应用运行所需的所有文件。解压后主要包含以下内容:

  1. classes.dex

    • 由 Java/Kotlin 代码编译后的 Dalvik/ART 字节码文件。

    • 如果方法数超过 65536,会生成多个 classes2.dexclasses3.dex 等(需启用 MultiDex)。

  2. resources.arsc

    • 编译后的二进制资源索引表,包含字符串、布局、颜色等资源的映射关系,用于快速查找资源。
  3. AndroidManifest.xml

    • 应用的配置文件(二进制格式),声明包名、权限、组件(Activity/Service 等)、最低 SDK 版本等。
  4. res/ 目录

    • 存放编译后的资源文件(图片、布局 XML、动画等),原始 XML 会被编译为二进制格式以优化读取效率。
  5. assets/ 目录

    • 存放原始资源文件(如字体、配置文件),通过 AssetManager 直接访问,不参与资源 ID 生成。
  6. lib/ 目录

    • 存放原生库(.so 文件),按 CPU 架构分目录(如 armeabi-v7a, arm64-v8a, x86)。
  7. META-INF/ 目录

    • 包含应用签名信息(MANIFEST.MF, CERT.SF, CERT.RSA),用于验证 APK 完整性。
  8. kotlin/ 目录

    • 如果使用 Kotlin,会包含 Kotlin 标准库的相关文件。
  9. 其他文件

    • 如 ProGuard/R8 生成的映射文件(mapping.txt)、AAPT2 生成的资源映射等。

二、APK 打包流程

Android 应用的构建流程通过 Gradle 和 Android 构建工具链(如 AAPT2、D8、R8 等)完成,主要步骤如下:

1. 资源处理
  • 工具:AAPT2 (Android Asset Packaging Tool)

    • 编译 res/ 下的资源文件(XML、图片等),生成 resources.arsc 和二进制 XML。

    • 生成 R.java 文件,为每个资源分配唯一 ID。

2. 代码编译
  • Java/Kotlin 编译

    • 将 Java/Kotlin 源代码编译为 .class 文件(javackotlinc)。
  • DEX 转换

    • 使用 D8 或 R8 工具.class 文件转换为 Android 虚拟机所需的 .dex 文件,优化字节码并可能启用代码混淆(通过 R8)。
3. 资源与代码合并
  • 工具:Android Gradle Plugin

    • 合并所有模块的资源文件,处理资源冲突。

    • classes.dexresources.arsclib/assets/ 等文件打包到临时 APK 中。

4. 原生库处理
  • 将 JNI 库(.so 文件)按 CPU 架构分类,并打包到 APK 的 lib/ 目录。
5. APK 签名
  • 工具:apksignerjarsigner

    • 使用开发者密钥对 APK 进行签名,确保应用来源可信且未被篡改。

    • 生成 META-INF/ 目录下的签名文件。

6. APK 对齐优化
  • 工具:zipalign

    • 对 APK 中的未压缩文件进行内存对齐(4 字节边界),提升运行时加载效率。

    • zipalign 主要工作是将apk包进行对齐处理。使apk包中的所有资源文件,起始偏移为4字节的整数倍,这样通过mmap内存映射访问apk时的速度会更快。

工具名称 功能介绍 在操作系统中的路径
aapt Android资源打包工具 ${ANDROID_SDK_HOME}/build-tools/30.0.0/aapt
aidl Android接口描述语言转化为.java文件的工具 ${ANDROID_SDK_HOME}/build-tools/30.0.0/aidl
javac java Compiler java代码转class文件 ${JDK_HOME}/javac或/usr/bin/javac
dex 转化.class文件为Davik VM能识别的.dex文件 ${ANDROID_SDK_HOME}/build-tools/30.0.0/dx
apkbuilder 生成apk包 ???没有找到
jarsigner .jar文件的签名工具 ${JDK_HOME}/jarsigner或/usr/bin/jarsigner
zipalign 字节码对齐工具 ${ANDROID_SDK_HOME}/tools/zipalign

三、详细构建流程图

复制代码
源代码 (Java/Kotlin)  --> 编译 --> .class 文件 --> D8/R8 --> classes.dex
资源文件 (res/, assets/) --> AAPT2 --> resources.arsc + 二进制 XML
原生库 (JNI) --> 按架构分类打包到 lib/
合并所有文件 --> 未签名 APK --> 签名 --> 签名后的 APK --> zipalign 对齐 --> 最终 APK

四、构建工具链演进

  • AAPT → AAPT2:支持增量资源编译,提升构建速度。

  • DX → D8:更快的 DEX 编译,更好的字节码优化。

  • ProGuard → R8:将代码压缩(Shrinking)、优化(Optimization)、混淆(Obfuscation)与 DEX 编译合并为一步。


五、优化与扩展

  • Android App Bundle (AAB):Google 推出的动态分发格式,按设备配置生成优化后的 APK。

  • Split APKs:根据屏幕密度、ABI 等拆分 APK,减少用户下载体积。

  • 资源混淆:通过工具(如 AndResGuard)对资源文件名进行混淆,进一步缩减 APK 体积。


**六、**虚拟机演进

虚拟机是一个可以运行 class , odex , oat 可执行文件的运行环境 ;

常见的虚拟机有 Java 虚拟机、Dalvik 虚拟机 、 ART 虚拟机 ;

Java 虚拟机 : 运行的 class 字节码文件 , 运行程序时解码 class 文件中的内容 ; 基于栈架构 , 需要频繁在栈上读写数据 , 造成较多的指令分派 , 更多的内存访问次数 , 比较耗费 CPU 时间 ;

编译时 : Java 源码 , 使用 javac 编译器 , 编译成 class 字节码文件 ; 运行时 : 类加载器通过 Java 类库验证字节码 , 验证通过会后进入 Java 虚拟机 , 进入 Java 解释器 或 即时编译器 , 然后进入运行时系统 , 之后进入操作系统 , 然后调用硬件 ;

Dalvik 虚拟机 : 基于 JIT 机制 ( 即时编译技术 )

Android 5.0 以下使用的虚拟机是 Dalvik 虚拟机 , 该虚拟机的可执行文件是 dex 文件 , 该文件比 class 字节码文件更小 ; JIT ( Just In Time ) 即时编译技术 , 对应 Dalvik 虚拟机 ; 基于寄存器架构 , 通过寄存器间接访问数据 , 该方式比基于栈架构速度更快 ;

ART 虚拟机 :

Android 5.0 以上使用的虚拟机是 ART 虚拟机 ; AOT ( Ahhead Of Time ) 预编译技术 , 对应 ART 虚拟机 ; Java 虚拟机 / Dalvik 虚拟机 / ART 虚拟机 都向上层提供了 3 个接口JNI_GetDefaultJavaVMInitArgs JNI_CreateJavaVM JNI_GetCreatedJavaVMS ; 虚拟机之间可实现无缝衔接 ;

Dalvik 虚拟机 与 ART 虚拟机区别 : 虚拟机中有个 persist.sys.dvlvik.vm.lib 字段 , 如果该字段存储的是 libdvm.so , 该虚拟机是 Dalvik 虚拟机 ; 如果该字段存储的是 ;ibart.so , 该虚拟机是 ART 虚拟机 ;

Dalvik 虚拟机 与 ART 虚拟机可执行文件 :

Dalvik 虚拟机加载 dex 文件加载时不是直接加载 dex 文件 , 加载执行的是 odex 文件 , odex 文件是通过 dexopt 工具对 dex 进行优化生成的 ;

ART 虚拟机加载 dex 文件时加载的是 oat 文件 , oat 文件时通过 dex2oat 工具对 dex 文件进行优化生成的 ;


通过理解 APK 的组成和打包流程,开发者可以更好地优化应用性能、调试构建问题,并掌握高级构建技术(如模块化、动态交付)。

参考:

1. 深入详解Apk编译打包流程

  1. APK 打包流程 ( 文件结构 | 打包流程 | 安装流程 | 安卓虚拟机 )

  2. Android 打包流程

相关推荐
学习同学1 分钟前
C++进阶知识复习 1~15
java·开发语言·c++
凭君语未可2 分钟前
详解Maven的主要生命周期
java·log4j·maven
bing_15816 分钟前
JVM 类加载器在什么情况下会加载一个类?
java·jvm
QING61830 分钟前
Kotlin containsValue用法及代码示例
android·kotlin·源码阅读
QING61834 分钟前
Kotlin coerceAtMost用法及代码示例
android·kotlin·源码阅读
QING61837 分钟前
Kotlin commonSuffixWith用法及代码示例
android·kotlin·源码阅读
QING61839 分钟前
Kotlin coerceAtLeast用法及代码示例
android·kotlin·源码阅读
冬天vs不冷2 小时前
EasyExcel导出自动回显中文,读取自动转换码值(基于全局转换器与自定义注解)
java·excel
阿里云云原生2 小时前
RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用
java·人工智能·spring
光军oi2 小时前
Mysql从入门到精通day5————子查询精讲
android·数据库·mysql