Android基础之apk的组成


前言

我们知道,生活中常见的手机品牌如华为、小米、三星、魅族、oppo、vivo等等均使用了Android系统(华为的鸿蒙系统也可兼容运行Android应用,苹果手机用的是IOS操作系统,不在讨论范围内)作为基础开发出了自家的ROM(鸿蒙及EMUI、MIUI、Flyme、Color OS、Funtouch OS等),这意味着它们可以运行Android的APP。而作为Android开发者,我们通常需要了解并掌握的便是Android开发需要的语言(javakotlin)以及Android提供给开发者使用的SDK(Software Development Kit的缩写,译作软件开发工具包)和开发工具Android Studio

那么一个Android的APP是怎样产生的呢?简单来讲,在APP开发经过需求分析、功能划分后,开发者利用Android Studio编写应用、经过编译构建后便可生成一个可安装调试的debug版apk文件,经过实机(模拟器)测试,修改调试,确认达到了预期目标后再通过正式的签名文件生成release版的apk文件,然后上传到手机应用市场,我们便可以下载安装各种APP来使用了。

apk的结构

APK是AndroidPackage的缩写,即Android安装包,其本质是一个zip压缩包,如果你有用过7zip、winRAR查看过apk文件,你将会发现如下图所示的目录结构:
1.png

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

作为参考,一个典型的apk文件包含以下内容:
2.png

apk各文件作用

AndroidManifest.xml

Android应用的配置清单文件,它向Android系统介绍了这个应用的很多配置信息,系统可以根据这个文件在相当程度上了解这个应用的一些信息。该文件是每个应用都必须定义和包含的,它描述了应用的名字、版本、权限、引用的库文件等等信息,如要把apk上传到Google Market上,也要对这个xml做一些配置。

在apk中的AndroidManifest.xml是经过压缩的,可以通过AXMLPrinter2或apktool等工具进行反编译查看。

META-INF目录

META-INF目录下存放的是签名信息,用来保证apk包的完整性系统安全。没有签名的应用是不被系统认可的,也无法安装到手机中。
Android SDK在对APK进行打包的时候,会把APK中全部文件的完整信息保存到这里,这样应用在安装的时候会进行完整性校验,确保APK的文件不会被篡改,大大提升了应用和系统的安全性与完整性。META-INF目录下有CERT.RSACERT.SFMANIFEST.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++就是更合适的选择了。

**注:**一般的Android开发是使用JavaKotlin调用SDK接口,SDK的底层实现其实也是调用的.so文件,若要自己生成.so文件以供调用则需要使用C/C++文件进行交叉编译后通过JNI(Java Native Interface)进行调用。

根据手机CPU的架构,lib库大体上可以分为4种:ARMARM-V7MIPSX86,分别对应着4种CPU架构,在lib目录里则分别是armeabiarmeabi-v7amipsx86一共4个目录。每个目录中的.so库名字都是一样的,实际上功能也是相同的,它们只是为了适配不同架构的CPU而存在。实际上,市面上的手机几乎全都是ARM架构的,所以大多数情况下我们只需要有armeabiarmeabi-v7a两种类型的库就足够了。

assets目录

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

classes.dex文件

classes.dex是Android系统的可执行文件,包含应用程序的全部操作指令以及运行时数据。
Android使用dalvik虚拟机(注意,Android5.0以后使用ART取代了dalvik,可简单认为是dalvik的升级版)运行java代码,但由于dalvik是一种针对嵌入式设备而特殊设计的java虚拟机,所以dex文件与标准的class文件在结构设计上有着本质的区别,不论是文件结构还是opcode都不一样。
在程序编译过程中,java源文件先被编译成class文件,然后通过dx工具将多个class文件整合为一个dex文件,这样的文件结构使得各个类能够共享数据,从而提高程序的运行效率。

**注:**其实dex也支持分包使用,分包后的文件需要重命名为classes数字序号.dex,如上文图片所示。

resources.arsc

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

debug版与release版的区别

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