Android基础之apk的组成
Android基础之apk的组成
前言
我们知道,生活中常见的手机品牌如华为、小米、三星、魅族、oppo、vivo等等均使用了Android系统
(华为的鸿蒙系统也可兼容运行Android应用,苹果手机用的是IOS操作系统,不在讨论范围内)作为基础开发出了自家的ROM(鸿蒙及EMUI、MIUI、Flyme、Color OS、Funtouch OS等),这意味着它们可以运行Android的APP。而作为Android开发者,我们通常需要了解并掌握的便是Android开发需要的语言(java
、kotlin
)以及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文件,你将会发现如下图所示的目录结构:
- assets目录: 存放需要打包到APK中的静态文件
- lib目录: 程序依赖的Native库
- META-INF目录: 存放应用程序签名和证书的目录
- res目录: 存放应用程序的资源
- AndroidManifest.xml: 应用程序的配置文件
- classes.dex: dex可执行文件
- resources.arsc: 资源配置文件
作为参考,一个典型的apk文件包含以下内容:
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.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++
就是更合适的选择了。
**注:**一般的Android开发是使用
Java
、Kotlin
调用SDK接口,SDK的底层实现
其实也是调用的.so
文件,若要自己生成.so
文件以供调用则需要使用C/C++
文件进行交叉编译
后通过JNI(Java Native Interface)
进行调用。
根据手机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是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通常称为发布版本
,是为用户使用的,一般客户不允许在发布版本上进行调试。所以不保存调试信息
,同时,它往往进行了各种优化
,以期达到代码最小和速度最优。为用户的使用提供便利。