Android APK文件结构 完整打包编译的流程 APK安装过程 详解

不管是 Android 知识的学习 还是其他知识的学习 ,最好还是去 官网 学习。因为现在信息年代,很多博客只是复制粘贴,有可能有哪些地方是错误的,会对我们造成误导,当然并不是说我就不复制粘贴了。当然官网介绍的并不详细,我也参考其他博客,把我们需要知道的一些东西也都放到本博客中了。
下面 是 Google Android 开发者平台 配置构建 部分内容翻译而来,网上很多 “Android打包编译的流程 ” 相关博客 也复制了官网的内容。
在这里插入图片描述

Android官网 配置构建 流程

Configure your build

The Android build system compiles app resources and source code, and 
packages them into APKs that you can test, deploy, sign, and distribute. 
Android Studio uses Gradle, an advanced build toolkit, to automate and 
manage the build process, while allowing you to define flexible custom 
build configurations. Each build configuration can define its own set of 
code and resources, while reusing the parts common to all versions of 
your app. The Android plugin for Gradle works with the build toolkit to 
provide processes and configurable settings that are specific to 
building and testing Android applications.

Android构建系统编译应用程序资源和源代码,并将它们打包成apk,您可以对其进行测试、部署、签名和分发。Android Studio使用高级构建工具包Gradle来自动化和管理构建过程,同时允许您定义灵活的自定义构建配置。每个构建配置都可以定义自己的代码和资源集,同时重用应用程序所有版本的通用部分。用于Gradle的Android插件与构建工具包一起工作,提供特定于构建和测试Android应用程序的流程和可配置设置。

Gradle and the Android plugin run independent of Android Studio. This 
means that you can build your Android apps from within Android Studio, 
the command line on your machine, or on machines where Android Studio is 
not installed (such as continuous integration servers). If you are not 
using Android Studio, you can learn how to build and run your app from 
the command line. The output of the build is the same whether you are 
building a project from the command line, on a remote machine, or using 
Android Studio.

Gradle和Android插件独立于Android Studio运行。这意味着您可以在Android Studio、您机器上的命令行或未安装 android studio 的机器(如持续集成服务器)上构建您的Android应用程序。如果您没有使用Android Studio,可以从命令行学习如何构建和运行应用程序。无论您是从命令行、在远程计算机上还是使用Android Studio生成项目,生成的输出都是相同的。

The build process

The build process involves many tools and processes that convert your 
project into an Android Application Package (APK). The build process is 
very flexible, so it's useful to understand some of what is happening 
under the hood.

构建过程涉及许多工具和过程,这些工具和过程会将您的项目转换为Android应用程序包(APK)。构建过程非常灵活,因此了解一些实际情况很有用。
在这里插入图片描述

Figure 1. The build process of a typical Android app module.

图1.典型的Android应用模块的构建过程。

The build process for a typical Android app module, as shown in figure 
1, follows these general steps:

	1. The compilers convert your source code into DEX (Dalvik Executable) 
files, which include the bytecode that runs on Android devices, and 
everything else into compiled resources.

	2.The APK Packager combines the DEX files and compiled resources into a 
single APK. Before your app can be installed and deployed onto an 
Android device, however, the APK must be signed.

	3.The APK Packager signs your APK using either the debug or release 
keystore:
	a. If you are building a debug version of your app, that is, an app 
	you intend only for testing and profiling, the packager signs your 
	app with the debug keystore. Android Studio automatically configures 
	new projects with a debug keystore.
	b. If you are building a release version of your app that you intend 	
	to release externally, the packager signs your app with the release 
	keystore. To create a release keystore, read about signing your app
	in Android Studio.

	4.Before generating your final APK, the packager uses the zipalign tool to optimize your app to use less memory when running on a device.

At the end of the build process, you have either a debug APK or release 
APK of your app that you can use to deploy, test, or release to external 
users.

如图1所示,一个典型的Android应用程序模块的构建过程遵循以下一般步骤:

  1. 编译器将您的源代码转换为DEX(Dalvik可执行文件)文件,其中包括在Android设备上运行的字节码,以及所有其他内容都转换为已编译资源。

  2. APK打包器将DEX文件和已编译的资源合并到一个APK中。但是,必须先对APK签名,然后才能将您的应用安装和部署到Android设备上。

  3. APK打包器使用调试或发布密钥库对您的APK进行签名:

    a. 如果您正在构建应用程序的调试版本,即仅打算进行测试和性能分析的应用程序,则打包程序将使用调试密钥库对应用程序进行签名。Android Studio使用调试密钥库自动配置新项目。

    b. 如果您要构建要从外部发布的应用程序的发行版本,则打包程序将使用发行密钥库对应用程序进行签名。

  4. 在生成最终APK之前,打包程序会使用zipalign工具优化您的应用,以在设备上运行时使用更少的内存。

在构建过程结束时,您可以拥有应用程序的调试APK或发布APK,可用于部署,测试或发布给外部用户。

Gradle 和 Android 插件独立于 Android Studio 运行。所以我们可以在 Android Studio上或者计算机上的命令行构建 Android 应用。 如果您不使用 Android Studio,可以学习如何从命令行构建和运行您的应用,最终构建的输出都相同。

APK文件结构

APK (Android Package)文件,是一个后缀名为.apk的压缩文件,APK文件中包含了一个Android应用程序的所有内容,是Android平台用于安装应用程序的文件。APK就是一个zip压缩包,解开这个APK包我们可以看到以下的结构:

  • assets 存放需要打包到APK中的静态文件
  • lib 存放应用程序所依赖的native库
  • res 存放应用程序的资源文件
  • META-INF 存放应用程序的签名和证书
  • AndroidManifest.xml 应用程序配置文件
  • classes.dex dex可执行文件
  • resources.arsc 资源索引表, 记录资源文件和资源ID之间的映射关系

assets

存放需要打包到APK中的静态文件,assets和res的区别如下,assets目录支持任意深度的子目录,用户可以根据自己的需求任意部署文件夹架构,而且res目录下的文件会在.R文件中生成对应的资源ID,assets不会自动生成对应的ID,访问的时候需要AssetManager类。

res

是resource的缩写,存放资源文件,存在这个文件夹下的所有文件都会映射到Android工程的.R文件中,生成对应的ID,访问的时候直接使用资源ID,即R.id.filename。
res 文件夹下可以包含多个文件夹,其中

  • anim存放动画文件;
  • drawable目录存放图像资源;
  • layout目录存放布局文件;
  • values目录存放一些特征值,colors.xml存放color颜色值,dimens.xml定义尺寸值,string.xml定义字符串的值,styles.xml定义样式对象;
  • xml文件夹存放任意xml文件,在运行时可以通过Resources.getXML()读取;
  • raw是可以直接复制到设备中的任意文件,他们无需编译。

lib

存放应用程序依赖的native库文件,一般是用C/C++编写,这里的lib库可能包含4中不同类型,根据CPU型号的不同,大体可以分为ARMARM-v7aMIPSX86,分别对应着ARM架构,ARM-V7架构,MIPS架构和X86架构。

不同的CPU架构对应着不同的目录,每个目录中可以放很多对应版本的so库,且这个目录的结构固定,用户只能按照这个目录存放自己的so库。目前市场上使用的移动终端大多是基于ARM或者ARM-V7a架构的,X86和MIPS架构的移动智能终端比较少,所以有些应用程序lib目录下只包含armeabi目录或者armeabi-v7a目录。

META-INF

保存应用的签名信息,签名信息可以验证APK文件的完整性。AndroidSDK在打包APK时会计算APK包中所有文件的完整性,并且把这些完整性保存到META-INF文件夹下,应用程序在安装的时候首先会根据META-INF文件夹校验APK的完整性,这样就可以保证APK中的每一个文件都不能被篡改。以此来确保APK应用程序不被恶意修改或者病毒感染,有利于确保Android应用的完整性和系统的安全性。

META-INF目录下包含的文件有CERT.RSACERT.DSACERT.SFMANIFEST.MF,其中CERT.RSA是开发者利用私钥对APK进行签名的签名文件,CERT.SF、MANIFEST.MF记录了文件中文件的SHA-1哈希值。

AndroidManifest.xml

是Android应用程序的配置文件,是一个用来描述Android应用“整体资讯”的设定文件,简单来说,相当于Android应用向Android系统“自我介绍”的配置文件,Android系统可以根据这个“自我介绍”完整地了解APK应用程序的资讯,每个Android应用程序都必须包含一个AndroidManifest.xml文件,且它的名字是固定的,不能修改。

我们在开发Android应用程序的时候,一般都把代码中的每一个Activity,Service,Provider和Receiver在AndroidManifest.xml中注册,只有这样系统才能启动对应的组件,另外这个文件还包含一些权限声明以及使用的SDK版本信息等等。

程序打包时,会把AndroidManifest.xml进行简单的编译,便于Android系统识别,编译之后的格式是AXML格式,如下图所示:
在这里插入图片描述
axml头:是固定标识axml文件的,其值固定是0x00080003。
axml文件长度:标识axml文件的大小。
StringDataSegment:xml文件中所有字符串类型保存在此。
ResourceIdSegment:xml文件中声明的资源文件ID保存于此。
XmlContentSegment:是xml的内容段,按照xml文件中的结构依次排开,保存xml的数据内容。

classes.dex

传统的Java程序,首先先把Java文件编译成class文件,字节码都保存在了class文件中,Java虚拟机可以通过解释执行这些class文件。

而Dalvik虚拟机是在Java虚拟机进行了优化,执行的是Dalvik字节码,而这些Dalvik字节码是由Java字节码转换而来,一般情况下,Android应用在打包时通过AndroidSDK中的dx工具将Java字节码转换为Dalvik字节码。

dx工具可以对多个class文件进行合并,重组,优化,可以达到减小体积,缩短运行时间的目的。dx工具的转换过程,如下图所示:
在这里插入图片描述
如上图所示,dx工具把每个.class文件的每个区域的内容进行去重,重组,优化重排后生成dex文件,生成的dex文件可以在Dalvik虚拟机执行,且速度比较快。

resources.arsc

用来记录资源文件和资源ID之间的映射关系,用来根据资源ID寻找资源。

Android的开发是分模块的,res目录专门用来存放资源文件,当在代码中需要调用资源文件时,只需要调用findviewbyId()就可以得到资源文件,每当在res文件夹下放一个文件,aapt就会自动生成对应的ID保存在.R文件,我们调用这个ID就可以,但是只有这个ID还不够,.R文件只是保证编译程序不报错,实际上在程序运行时,系统要根据ID去寻找对应的资源路径,而resources.arsc文件就是用来记录这些ID和资源文件位置对应关系的文件。

Android完整打包流程

  • aapt阶段:aapt工具编译res资源文件,把大部分xml文件编译成二进制文件(图片文件除外),同时生成R.Java文件和resources.arsc文件,里面保存了资源的ID和在APK中的路径。
  • aidl阶段:如果项目中有使用AIDL,那么就会把.aidl文件编译成.java文件。
  • javaCompiler阶段:将所有.java文件(包括R文件和AIDL生成的.java文件),通过javac工具生成class文件。
  • dex阶段:将生成的.class文件和第三方库的.class文件通过dx工具生成classes.dex文件(如果有分包,那么可能有多个)。
  • apkBuilder阶段:aapt阶段中的资源文件、dex文件和第三方的非java资源包(.so),通过apkbuilder工具生成未签名的apk包。
  • jarSigner阶段:签名,jarsigner工具,如果是debug模式用默认签名,release模式用开发者的签名。
  • zipAligin阶段:对齐,通过zipalign工具对apk中的未压缩资源(图片、视频)进行“对齐操作”,让资源按4字节的边界进行对齐,使得资源访问速度更快。

流程图如下:
在这里插入图片描述
在这里插入图片描述

详细介绍

1. aapt阶段

aapt.exe工具(The Android Asset Packaing Tool),位于android-sdk/platform-tools目录下。

  1. 使用aapt来打包res资源文件,生成 R.javaresources.arscres文件,res文件分为 二进制 和 非二进制 文件,典型的非二进制文件如:res/raw和图片,它们保持原样,不被编译。
  2. res目录有9种目录,如下:
    - animator :这类资源以XML文件保存在res/animator目录下,用来描述属性动画
    - anim:这类资源以XML文件保存在res/anim目录下,用来描述补间动画
    - color:这类资源以XML文件保存在res/color目录下,用描述对象颜色状态选择。
    - drawable:这类资源以XML或者Bitmap文件保存在res/drawable目录下,用来描述可绘制对象。例如,我们可以在里面放置一些图片(.png, .9.png, .jpg, .gif),来作为程序界面视图的背景图。注意,保存在这个目录中的Bitmap文件在打包的过程中,可能会被优化的。例如,一个不需要多于256色的真彩色PNG文件可能会被转换成一个只有8位调色板的PNG面板,这样就可以无损地压缩图片,以减少图片所占用的内存资源。
    - layout:这类资源以XML文件保存在res/layout目录下,用来描述应用程序界面布局。
    - menu:这类资源以XML文件保存在res/menu目录下,用来描述应用程序菜单。
    - raw:这类资源以任意格式的文件保存在res/raw目录下,它们和assets类资源一样,都是原装不动地打包在apk文件中的,不过它们会被赋予资源ID,这样我们就可以在程序中通过ID来访问它们。例如,假设在res/raw目录下有一个名称为filename的文件,并且它在编译的过程,被赋予的资源ID为R.raw.filename,那么就可以使用以下代码来访问它:Resources res = getResources(); InputStream is = res .openRawResource(R.raw.filename);
    - values:这类资源以XML文件保存在res/values目录下,用来描述一些简单值,例如,数组、颜色、尺寸、字符串和样式值等,一般来说,这六种不同的值分别保存在名称为arrays.xml、colors.xml、dimens.xml、strings.xml和styles.xml文件中。
    - xml:这类资源以XML文件保存在res/xml目录下,一般就是用来描述应用程序的配置信息。
  3. R.java 文件
    R.java 文件是自动生成的,如下:
    在这里插入图片描述
    在这里插入图片描述
    R.java文件路径如下:
    在这里插入图片描述
    可以看出R.java文件里面拥有很多个静态内部类,比如layout,string等。每当有这种资源添加时,就在R.java文件中对应类型的静态内部类中添加一个public static final int 类型成员。
    R.java文件里面的资源可以有两种方法引用:
    (1)在java程序中引用资源按照java的语法来引用,R.resource_type.resource_ name,注意:resource_name不需要文件的后缀名,例如R.id.text_input_password_toggleR.drawable.shadow_left
    (2)在XML文件中引用资源格式:@[package:] type/name,如下图:
    在这里插入图片描述
  4. resources.arsc文件
    resources.arsc这个文件记录了所有的应用程序资源目录的信息,包括每一个资源名称、类型、值、ID以及所配置的维度信息。我们可以将这个resources.arsc文件想象成是一个资源索引表,这个资源索引表在给定资源ID和设备配置信息的情况下,能够在应用程序的资源目录中快速地找到最匹配的资源。

2. aidl阶段

这一过程中使用到的工具是aidl.exe,位于android-sdk/platform-tools目录下。
AIDL (Android Interface Definition Language), Android接口定义语言,Android提供的IPC (Inter Process Communication,进程间通信)的一种独特实现。
这个阶段处理.aidl文件,生成对应的.java文件。如果在项目没有使用到aidl文件,则可以跳过这一步。

3. Java Compiler阶段

通过Java Compiler 编译项目中所有的Java代码,包括R.java、.aidl文件生成的.java文件、Java源文件,生成.class文件。

4. dex阶段

dx工具位于android-sdk/platform-tools 目录下,通过它生成可供Android系统Dalvik虚拟机执行的classes.dex文件。

在这个阶段任何第三方的libraries和.class文件都会被转换成.dex文件。
dx工具的主要工作是将Java字节码转成成Dalvik字节码、压缩常量池、消除冗余信息等。

5. apkBuilder阶段

打包的工具apkbuilder位于 android-sdk/tools目录下。apkbuilder为一个脚本文件,实际调用的是android-sdk/tools/lib/sdklib.jar文件中的com.android.sdklib.build.ApkbuilderMain类。

所有没有编译的资源(如 res/raw、images等)、Other Resources(assets文件)、编译过的资源 、.dex文件 、resources.arsc 和 AndroidManifest.xml 都会被apkbuilder工具打包到最终的.apk文件中。

注意
res/raw和assets的相同点:两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。
res/raw和assets的不同点:
1.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
2.res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

6. JarSigner阶段

一旦apk文件生成,它必须被签名才能被安装在设备上。

在开发过程中,主要用到的就是两种签名的keystore。一种是用于调试的debug.keystore,它主要用于调试。另一种就是用于发布正式版本的keystore。

7. zipAlign阶段

如果你发布的apk是正式版的话,就必须对APK进行对齐处理,用到的工具是zipalign,它位于android-sdk/tools目录下。

Zipalign是一个android平台上整理APK文件的工具,它对apk中未压缩的数据进行4字节对齐,对齐的主要过程是将APK包中所有的资源文件距离文件起始偏移为4字节整数倍,对齐后就可以使用mmap函数读取文件,可以像读取内存一样对普通文件进行操作。如果没有4字节对齐,就必须显式的读取,这样比较缓慢并且会耗费额外的内存。

打包工具对应截图如下:
在这里插入图片描述
在这里插入图片描述

APK安装过程

Adroid的应用安装涉及到如下几个目录:

  • /data/app:存放用户安装的APK的目录,安装时,把APK拷贝于此。
  • /data/data:应用安装完成后,在/data/data目录下自动生成和APK包名(packagename)一样的文件夹,用于存放应用程序的数据。
  • /data/dalvik-cache:存放APK的odex文件,便于应用启动时直接执行。

具体安装过程如下:

  1. 复制APK安装包到/data/app下,然后校验APK的签名是否正确,检查APK的结构是否正常,进而解压并且校验APK中的dex文件。
  2. 确定dex文件没有被损坏后,再把dex优化成odex,使得应用程序启动时间加快。
  3. 同时在/data/data目录下建立于APK包名相同的文件夹,如果APK中有lib库,系统会判断这些so库的名字,查看是否以lib开头,是否以.so结尾,再根据CPU的架构解压对应的so库到/data/data/packagename/lib下。

APK安装的时候会把DEX文件解压并且优化位odex,odex的格式如下图所示:
在这里插入图片描述
如上图所示,odex在原来的dex文件头添加了一些数据,在文件尾部添加了程序运行时需要的依赖库和辅助数据,使得程序运行速度更快。

发布了18 篇原创文章 · 获赞 1 · 访问量 681

猜你喜欢

转载自blog.csdn.net/aha_jasper/article/details/104944929