在开始解析 Android 构建流程之前,我们先来看下构建的最终产物 APK 的整体组成:
APK 主要由五个部分组成,分别是:
工欲善其事,必先利其器,既然想分析 APK 必然少不了好用的工具。
① Android Studio 自带的 APK 分析器
通过 APK 分析器,我们可以完成这些操作:
② ClassyShark 可以做为 AS 自带 APK 分析器的补充,帮我们分析 dex 中的详细数据,以及查看 APK 中的总方法数以及各个模块的方法数分布。
当我们在 Android 查看一个 APK 的时候,可以看到右上角有 Defined Methods 和 Referenced Methods,但大多数人可能不知道这两者的区别,这里简单说明下:
Defined Methods:在这个 Dex 中定义的方法;Referenced Methods:Defined Methods 以及 Defined Methods 引用到的方法。
Android 有 64K 引用限制,当 type_ids、method_ids 或者 field_ids 超过 65536(64 * 1024)的时候,需要进行 dex 分包,为了 Dex 的数量尽可能少,我们需要尽量实现「Dex 信息有效率」的提升。
Dex 信息有效率 = Defined Methods 数量 / Referenced Methods 数量
当我们用 Android Studio 进行安装包构建的时候,会发现其实是运行了一连串的 Task,也就是说其实是这些 task 的配合,最终构建出我们的 APK 的。
如果我们想更了解 Android 的构建流程,对于相关的源码肯定是要有所了解的。那我们如何看到这些 Task 相关的源码呢,我们知道 Android 是用 Gradle 进行构建的,也就意味着这些 task 其实都是放在 Gradle 中,我们想看 Gradle 中源码的话,可以在 build.gradle 将 Gradle 进行编译。
compileOnly "com.android.tools.build:gradle:3.0.1"
编译完之后,可以在 ApplicationTaskManager#createTasksForVariantScope 中找到创建这些 Task 相关的代码,也就意味着顺藤摸瓜找到这些 Task 的真正实现逻辑。
这里以 BuildConfig 文件的生成为例,来梳理下如何查看某个 task 的代码逻辑。
生成 BuildConfig 文件,是通过 ApplicationTaskManager 中通过 createBuildConfigTask 来创建对应的 task。
顺着代码逻辑,我们找到最终真正实现这个逻辑的是:GenerateBuildConfig 这个 task,GenerateBuildConfig 是继承自 BaseTask,这里有个小技巧是,Task 中真正的执行逻辑都是在带着 @TaskAction 注解的方法上的,所以我们能很快找到对应的 generate() 方法。可以看到生成 BuildConfig 整体的逻辑还是比较简单的,其实就是将 build.gradle 中自带的属性以及我们自定义的属性进行读取,然后通过 JavaWriter 生成对应的 BuildConfig 文件。
看到上面的例子,可能有些人会抛出一个疑问就是那我们怎么确定构建中执行的 task 具体对应哪个类呢,这里提供一个小技巧,其实我们可以在 taskGraph 构建完成之后,将所有 task name 以及对应的 class 进行打印。例如在 build.gradle 中加入这个代码之后,我们在运行的时候,就会把 task 所对应的类名也都一起打印出来。
可以看到 Android 构建中会涉及到多个工具,我们可以通过 open $ANDROID_HOME/build-tools 来查看相关的构建工具。
最后我们通过命令行来手动打包一个可执行的 APK,能让我们对 APK 构建的理解更加深入。首先需要准备下 代码、资源文件、AndroidManifest 这些构建 APK 的必要文件。
① 通过 aapt2 compile 将 res 资源编译成 .flat 的二进制文件:
aapt2 compile -o build/res.zip --dir res
② 通过 aapt2 link 将 .flat 和 AndroidManifest 进行连接,转化成不包含 dex 的 apk 和 R.java:
aapt2 link build/res.zip -I $ANDROID_HOME/platforms/android-30/android.jar --java build --manifest AndroidManifest.xml -o build/app-debug.apk
③ 通过 javac 将 Java 文件编译成 .class 文件:
javac -d build -cp $ANDROID_HOME/platforms/android-30/android.jar com/**/**/**/*.java
④ 通过 d8 将 .class 文件转化成 dex 文件:
d8 --output build/ --lib $ANDROID_HOME/platforms/android-30/android.jar build/com/tencent/hockeyli/androidbuild/*.class
⑤ 合并 dex ⽂件和资源⽂件:
zip -j build/app-debug.apk build/classes.dex
⑥ 对 apk 通过 apksigner 进行签名:
apksigner sign -ks ~/.android/debug.keystore build/appdebug.apk
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8