Android 应用是如何编译与运行的

Android Studio 负责如何构建与部署我们的应用。但是你有没有想过当你按下Run按钮时发生了什么?

构建

1. Java 编译

我们的代码是用Java编写的。但是,Java代码的编译和运行方式与Web应用程序相同吗?

Android应用程序的编译过程与其他Java应用程序有很大不同。

但是它们的开始过程都是一样的:

使用javac命令将Java源代码文件编译为.class文件。

Android 应用是如何编译与运行的_第1张图片

它会将下面的java代码:

public MainActivity() {
  super();
  currentPosition = 0;
}

转换成这样的java字节码:

public com.hfad.bitsandpizzas.MainActivity();
  Code:
   0:	aload_0
   1:	invokespecial	#5; //Method android/app/Activity."":()V
   4:	aload_0
   5:	iconst_0
   6:	putfield	#3; //Field currentPosition:I
   9:	return

2. 转换为 Dalvik 字节码

.class文件包含标准的Oracle JVM Java字节码。但Android设备不使用此字节码格式。相反,Android有自己独特的字节码格式,称为Dalvik。

Dalvik字节码,与Oracle JVM字节码一样,是理论处理器的机器代码指令。

编译过程需要将.class文件和任何.jar库转换为包含Dalvik字节码的单个classes.dex文件。这是通过dx命令完成的:

Android 应用是如何编译与运行的_第2张图片

dx命令将所有.class和.jar文件拼接成一个以Dalvik字节码格式编写的classes.dex文件。

0x0000: iput-object v1, v0, Lcom/hfad/bitsandpizzas/MainActivity; com.hfad.bitsandpizzas.MainActivity$2.this$0 // field@4869
0x0002: invoke-direct {v0}, void java.lang.Object.() // method@13682
0x0005: return-void

3. 将 classes.dex 与 资源文件打包到 apk 文件中

然后将classes.dex文件和应用程序中的资源(如图像和布局)压缩为类似zip的文件,称为Android Package或.apk文件。这是通过 Android Asset Packaging Tool 或 aapt 完成的:

Android 应用是如何编译与运行的_第3张图片

这个步骤完成之后,.apk文件就可以安装了。但是,还有一个步骤需要做…

4. 给 apk 文件签名

如果想要通过Google Play商店分发应用,则需要对其进行签名。对应用程序包进行签名意味着您在.apk中存储了一个附加文件,该文件基于.apk内容的校验和以及单独生成的私钥。

.apk文件使用标准的jarsigner工具,该工具是Oracle Java Development Kit的一部分。创建jarsigner工具是为了签署.jar文件,但它也可以使用.apk文件,因为它们也是压缩文件。

如果您对.apk文件进行签名,则还需要通过名为zipalign的工具运行它,这将确保文件的压缩部分在字节边界上排列。 Android希望它们按字节对齐,以便它可以轻松读取它们而无需解压缩文件。

Android 应用是如何编译与运行的_第4张图片

网上还有上面所有步骤的详细图:

Android 应用是如何编译与运行的_第5张图片

部署

1. 启动 adb 服务

该应用程序将通过Android Debug Bridge部署到Android设备。

在我们的开发端上开启一个 adb 服务进程,在 Android 设备上开启一个类似的 adb 服务(adbd)。

如果您的计算机上未运行adb进程,则adb命令将启动它。

Android 应用是如何编译与运行的_第6张图片

adb进程将打开网络套接字,并在端口5037上侦听命令。您输入的每个adb命令都会将其指令发送到此端口。

2. 将文件传输到设备

adb命令用于将.apk文件传输到Android设备上的文件系统中。该位置由应用程序的包名定义。

因此,例如,如果包是com.hfad.bitsandpizzas,则.apk文件将放在/data/app/com.hfad.bitsandpizzas中。

Android 应用是如何编译与运行的_第7张图片

运行

Android应用程序的运行方式最近发生了变化。

从API级别21开始,旧的Dalvik虚拟机已被新的Android Runtime取代。

让我们看一下应用程序运行时一步一步发生的事情。

用户启动app

一个名为Zygote的过程用于启动应用程序。 Zygote是Android进程的不完整版本 —— 其内存空间包含任何应用程序所需的所有核心库,但它尚未包含特定于特定应用程序的任何代码。 Zygote使用fork系统调用创建自己的副本。

Android是一个Linux系统,fork调用可以很快复制像Zygote这样的进程。这就是使用Zygote进程的原因:复制像Zygote这样的半启动进程比从主系统文件加载新进程要快得多。 Zygote意味着您的应用程序启动速度更快。

Android 应用是如何编译与运行的_第8张图片

将 dex 文件转换成 oat 文件

新的app进程现在需要加载我们的应用程序的代码。请记住,您的应用代码存储在.apk包中的classes.dex文件中。因此,会从.apk中提取classes.dex文件并放入一个单独的目录中。但是,不是简单地放置classes.dex文件的副本,Android会将classes.dex中的Dalvik字节码转换为本机机器码。

所有以Java代码开头的代码现在都转换为一段本机编译代码。从技术上讲,classes.dex将转换为ELF共享对象。 Android调用此库格式OAT,转换classes.dex文件的工具称为dex2oat。(Android 5.0 开始,dex -> oat 这个转换过程发生在安装过程中

Android 应用是如何编译与运行的_第9张图片

转换后的文件存储在如下的目录中:

/data/dalvik-cache/x86/data@[email protected]@[email protected]

该路径将包含应用程序的包名称,以确保它不会覆盖任何其他应用程序。

转换后的代码将在特定于Android设备CPU的机器代码中。例如,如果Android设备是x86,则OAT文件将如下所示:

0x001db888:         85842400E0FFFF    	test    eax, [esp + -8192]
suspend point dex PC: 0x0000
GC map objects:  v0 (r5), v1 (r6)
0x001db88f:                 83EC1C    	sub     esp, 28
0x001db892:               896C2410    	mov     [esp + 16], ebp
0x001db896:               89742414    	mov     [esp + 20], esi
0x001db89a:               897C2418    	mov     [esp + 24], edi
0x001db89e:                   8BF8    	mov     edi, eax
0x001db8a0:                 890424    	mov     [esp], eax
...

app 加载本地机器码

然后将 native library 直接映射到应用程序进程的内存中。

Android 应用是如何编译与运行的_第10张图片

此时开始,应用程序将启动初始界面,应用程序将出现在屏幕上。

你可能感兴趣的:(Android-每日一问)