了解 APK 到底是什么

全称:Android application package,Android应用程序包,是一个标准的 ZIP 文件,狭义上说,他不是可执行文件,linux 上可执行文件是 ELF 文件,但是 APK 不是 ELF 文件。因此 aaa.apk == aaa.zip

一个APK文件通常包含以下文件:

  • META-INF文件夹:用于保存 App 的签名和校验信息,以保证程序的完整性。当生成 APK 包时,系统会对包中的所有内容做一次校验,然后将结果保存在这里。而手机在安装这一 App 时还会对内容再做一次校验,并和 META-INF 中的值进行比较,以避免 APK 被恶意篡改。其中包含如下 三个文件,如下所示:
    • 1)、MANIFEST.MF:其中每一个资源文件都有一个对应的 SHA-256-Digest(SHA1) 签名,MANIFEST.MF 文件的 SHA256(SHA1) 经过 base64 编码的结果即为 CERT.SF 中的 SHA256(SHA1)-Digest-Manifest 值。
    • 2)、CERT.SF:除了开头处定义的 SHA256(SHA1)-Digest-Manifest 值,后面几项的值是对 MANIFEST.MF 文件中的每项再次 SHA256(SHA1) 经过 base64 编码后的值。
    • 3)、CERT.RSA:其中包含了公钥、加密算法等信息。首先,对前一步生成的 CERT.SF 使用了 SHA256(SHA1)生成了数字摘要并使用了 RSA 加密,接着,利用了开发者私钥进行签名。然后,在安装时使用公钥解密。最后,将其与未加密的摘要信息(MANIFEST.MF文件)进行对比,如果相符,则表明内容没有被修改。
  • res: APK所需要的资源文件夹。
  • AndroidManifest.xml: 一个传统的Android清单文件,用于描述该应用程序的名字、版本号、所需权限、注册的服务、链接的其他应用程序。
  • classes.dex: classes文件通过DEX编译后的文件格式,用于在Dalvik虚拟机上运行的主要代码部分。
  • resources.arsc: 资源索引表文件,通过该文件对资源进行定位。也可以用ApkTool等工具反编译后再开始进行软件修改

了解 .java 文件是如何变成 .dex 文件的,说一说用到的工具

先了解一下 JVM,Dalvik 和 ART。

  • JVM 是 JAVA 虚拟机,用来运行 JAVA 字节码程序。
  • Dalvik 是 Google 设计的用于 Android 平台的运行时环境,适合移动环境下内存和处理器速度有限的系统。
  • ART 即 Android Runtime,是 Google 为了替换 Dalvik 设计的新 Android 运行时环境,在 Android 4.4 推出。ART 比 Dalvik 的性能更好。

Dalvik 虚拟机不支持直接执行 JAVA 字节码,所以会对编译生成的 .class 文件进行翻译、重构、解释、压缩等处理,这个处理过程是由 dx/d8/r8(这是一个工具,位置为$ANDROID_HOME/build-tools/(不同版本号)/dx) 进行处理,处理完成后生成的产物会以 .dex 结尾,称为 Dex 文件。
Dex 文件格式是专为 Dalvik 设计的一种压缩格式。

那么可以知道,

  • JVM的输入是java文件,输出是class文件
  • Dalvik的输入时class文件,输出是dex文件,Dex 文件是很多 .class 文件处理后的产物

.java 通过 javac 变成 .class 文件,.class 文件再经过 dx/d8/r8 变成 .dex 文件

首先在java中,java文件通过javac命令编译生成.class文件,dex文件的流程也是类似。
假设我们编写了一个HelloWorld程序,并且只有一个main函数,main函数里只有打印一个HelloWorld,通过javac生成.class文件后,再通过 dx 工具的如下命令:

  1. $ANDROID_HOME/build-tools/28.0.3/dx --dex --output=classes.dex HelloWorld.class

可以输出一个classes.dex文件,拿到dex文件后,我们需要把他放到一个可以运行它的OS,就是Android系统,我们直接push到手机上,下一步通过如下命令:

  1. dalvikvm -cp HelloWorld.dex HelloWorld

可以直接运行HelloWorld程序,并输出HelloWorld,其中 cp 指定的是 classpath,后面指定的类名,毕竟 dex 文件一旦有多个类存在 main 函数的话,就不知道选哪个类去运行了。

Dalvik虚拟机 除了能接受一个裸露的 dex 文件以外,还能接受一个 zip 格式的文件,只要求里面的 dex 文件名必须是 classes.dex 就行或者是一个zip文件解压出来有dex文件。比如我们传一个 zip/apk/jar 都能接受,毕竟他们的本质都是 zip。

了解资源是如何被编译的,说一说需要的源文件和用到的工具

资源文件指的就是res下除了raw的文件和 AndroidManifest.xml ,通过 aapt/aapt2(文件位于SDK下的build-tools中) 一起编译和链接出相应的二进制版本

AAPT2(Android 资源打包工具)是一个构建工具,Android Studio 和 Android Gradle Plugin 使用它来编译和打包应用的资源。AAPT2 会解析资源、为资源编制索引,并将资源编译为针对 Android 平台进行过优化的二进制格式。

从 Android Gradle Plugin 3.0.0 开始,AAPT2 默认开启,相对于 AAPT,资源打包流程由原来的单一编译过程拆分为「编译」和「链接」两个阶段。

怎么把上面两个东西组合到一起?组合出来的东西是什么?放到手机上运行还差什么?

首先,一个apk的结构大致如下,

  • classes.dex
  • 资源文件
  • resources.arsc
  • 签名摘要
  • 可选的 assets 等

前三个在前面已经单独编译过,现在需要整合他们。也就是通过appt2得到的文件重命名为 apk后 ,通过如下命令整合dex文件

  1. zip -ur app-debug.apk classes.dex

这样就得到了一个未签名的apk,app-unsigned.apk,下一步就是对apk签名,可以通过 apksigner 工具,使用 android debug key进行签名

签名成功后,就可以安装到手机上了。