APK包结构及打包流程

先来介绍一下安卓apk包结构的目录
实际上,一个APK文件就是一个.zip格式的压缩包,我们可以用解压缩工具打开任何一个APK文件,由于代码混淆和加密,通过普通解压缩工具打开里面的文件或目录会看到各种乱码。一个典型的apk文件包含以下内容:

Android应用程序APK文件的结构图
在这里插入图片描述

  • assets目录 存放需要打包到APK中的静态文件
  • lib目录 程序依赖的Native库
  • res目录 存放应用程序的资源
  • META-INF目录 存放应用程序签名和证书的目录
  • AndroidManifest.xml 应用程序的配置文件
  • classes.dex dex可执行文件
  • resources.arsc 资源配置文件

- AndroidManifest.xml

Android应用的配置清单文件,它向Android系统介绍了这个应用的很多配置信息,系统可以根据这个文件在相当程度上了解这个应用的一些信息。该文件是每个应用都必须定义和包含的,它描述了应用的名字、版本、权限、引用的库文件等等信息,如要把apk上传到Google Market上,也要对这个xml做一些配置。在apk中的AndroidManifest.xml是经过压缩的,可以通过AXMLPrinter2工具(针对该文件)或apktool工具进行反编译(反编译整个apk)。

- META-INF目录

META-INF目录下存放的是签名信息,用来保证apk包的完整性和系统的安全。没有签名的应用是不被系统认可的,也无法安装到手机中。Android SDK在对APK进行打包的时候,会把APK中全部文件的完整信息保存到这里,这样应用在安装的时候会进行完整性校验,确保APK的文件不会被篡改,大大提升了应用和系统的安全性与完整性。META-INF目录下有CERT.RSA、CERT.SF和MANIFEST.MF这几个文件,其中的CERT.RSA文件记录了开发者的私钥对APK签名后的信息,MANIFEST.MF文件则保存了整个APK中所有文件的SHA-1进行base64编码后的值,CERT.SF则与MANIFEST.MF差不多,包括了后者所有的信息,然后又加入了MANIFEST.MF文件的SHA-1并base64编码的值。

- res目录

存放各种资源文件的目录。这个目录中的所有文件,最终会被映射到Android工程中的R文件中,生成对应的int型的ID,在程序中访问这些资源文件的时候,直接使用资源的ID就能进行调用了。
res目录下还包含着多个子文件夹:anmi中存放着动画文件;drawable则存放着一些图片资源;layout中存放的是布局文件;menu则是自定义菜单的项;raw目录中的文件则是可以直接复制到设备中的文件,不会被编译;values中存放着一些特殊的值——colors.xml记录的是你自定义的颜色,dimens.xml记录着你自定义的尺寸,strings.xml则是你自定义的字符串常量值,styles.xml定义了一些样式。

- lib目录

这个目录中存放着应用依赖的native库文件,这些以.so结尾的文件是用C或者C++语言编写的,一个简单的Android应用可能并不需要这些库,但一个功能全面而又追求性能的应用是不可能无视这个目录的,譬如图片处理、网络处理、音视频处理等一些对性能要求很高的功能,单纯依靠Java会十分吃力,性能更加强大而且更加接近底层的C/C++就是更合适的选择了。
根据手机CPU的架构,lib库大体上可以分为4种:ARM、ARM-V7、MIPS和X86,分别对应着4种CPU架构,在lib目录里则分别是armeabi、armeabi-v7a、mips和x86一共4个目录。每个目录中的.so库名字都是一样的,实际上功能也是相同的,它们只是为了适配不同架构的CPU而存在。实际上,市面上的手机几乎全都是ARM架构的,所以大多数情况下我们只需要有armeabi和armeabi-v7a两种类型的库就足够了。

- assets目录

跟res目录有点相似,但实际上二者还是有区别的。res目录中的文件会映射到R文件中,每个资源文件都有自己的ID,而assets中的文件则直接通过访问文件的地址来使用AssetManager类进行访问,而且assets目录你可以添加任意深度的子目录,这一点会比较方便管理和归类文件。相比较之下,res目录目前不能支持更深级的子目录。

- classes.dex文件
classes.dex是java源码编译后生成的java字节码文件(首先是java文件通过jdk编译成字节码文件然后经过dex编译成classes.dex)。但由于Android使用的dalvik虚拟机与标准的java虚拟机是不兼容 的,dex文件与class文件相比,不论是文件结构还是opcode都不一样。目前常见的java反编译工具都不能处理dex文件。Android模拟 器中提供了一个dex文件的反编译工具,dexdump。用法为首先启动Android模拟器,把要查看的dex文件用adb push上传的模拟器中,然后通过adb shell登录,找到要查看的dex文件,执行dexdump xxx.dex。

对比普通java程序和安卓程序差异
普通java程序 java虚拟机 java字节码(class) 基于栈
安卓程序 Davlik虚拟机 Dalvik字节码(dex) 基于寄存器

- resources.arsc
编译后的二进制资源文件的索引,记录了资源文件(即res目录中的文件)和资源文件ID的映射关系,这样程序运行的时候就可以根据资源的ID获取到相应的资源了。

APK打包流程
网上找的图:
在这里插入图片描述
谷歌官方文档的图及介绍:在这里插入图片描述
一个典型的Android应用模块的构建过程遵循以下一般步骤:

1、编译器将您的源代码转换为DEX(Dalvik可执行文件)文件,其中包括在Android设备上运行的字节码,以及所有其他内容都转换为已编译资源。2、APK打包器将DEX文件和已编译的资源合并到一个APK中。但是,必须先对APK签名,然后才能将您的应用安装和部署到Android设备上。
3、APK Packager使用调试或释放密钥库对您的APK进行签名:
a、如果要构建应用程序的调试版本,即仅打算进行测试和性能分析的应用程序,则打包程序将使用调试密钥库对应用程序进行签名。Android Studio使用调试密钥库自动配置新项目。
b、如果您要构建要从外部发布的应用程序发行版本,则打包程序将使用发行密钥库对应用程序进行签名。
4、在生成最终APK之前,打包程序会使用zipalign工具优化您的应用,以在设备上运行时使用更少的内存

Debug包和Release包对比

再来看看debug包和release包对比,话不多说,先上图
在这里插入图片描述
在这里插入图片描述
左边是release包,右边是debug包,可以看到,一样的打包配置,debug包要比release包要大0.3mb。主要大在dex文件和res文件夹,其他都一样

我们继续看看dex文件区别在哪里:
在这里插入图片描述
有android、androidx、kotlin、test四个文件夹大小不一样,其他文件夹大小均一样,我们再来看看android文件夹
在这里插入图片描述
我们可以看到,有定义方法的文件夹,他们的大小就是不一样的,再往里看
在这里插入图片描述
可以看到support包下的v4、v7等文件夹大小均不一样,再往下看
在这里插入图片描述
打开对比看看这个类的字节码
在这里插入图片描述
左边的debug包的字节码行数要比右边的release包字节码行数要多100多行(至于为什么会是这样,,我猜测是debug模式下要多一些调试信息吧)

我们再来看看res文件夹,drawable-xxx-v4的文件夹大小不一样在这里插入图片描述
再往下看,发现debug包里面ic_launcher_background图片文件大小和release包里面ic_launcher_background大小不一样,一个是2.75kb,一个是767b,换算一下正好是drawable-xxhdpi-v4文件夹大小的区别,,其他几个drawable文件夹也类似,至于为什么谷歌官方会把debug包和release包相同的文件用不同的大小,我也不得而知,我猜测是release包进行了压缩处理之类

在这里插入图片描述
结论毫无疑问,相同代码配置情况下,debug包会比release包大一点,应该是debug包包含了一些调试信息,而release包进行了一些优化,以期达到代码最小和速度最优

摘自此篇
Debug通常称为调试版本,通过一系列编译选项的配合,编译的结果通常包含调试信息,而且不做任何优化,以为开发 人员提供强大的应用程序调试能力。
Release通常称为发布版本,是为用户使用的,一般客户不允许在发布版本上进行调试。所以不保存调试信 息,同时,它往往进行了各种优化,以期达到代码最小和速度最优。为用户的使用提供便利。

(1) debug程序通常比release程序要慢,尤其是处理视频方便release要比debug快很多。在release模式对程序进行调试的时候经常会遇到变量虽然初始化了,但是在查看其值的时候却发现是一个随机的数并不是初始化的值,有时候在对变量进行监视的时候了,会出现找不到变量的情况。
(2) debug跟release在初始化变量时所做的操作是不同的,debug是将每个字节位都赋成0xcc, 而release的赋值近似于随机。在声明变量后马上对其初始化一个默认的值是最简单有效的办法,否则项目大了你找都没地方找。代码存在错误在debug方式下可能会忽略而不被察觉到。debug方式下数组越界也大多不会出错,在release中就暴露出来了,这个找起来就比较难了。
(3) 只有DEBUG版的程序才能设置断点、单步执行、使用 TRACE/ASSERT等调试输出语句。REALEASE不包含任何调试信息,所以体积小、运行速度快。

至此,我们对apk的研究结束

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐