一款 APK 是怎麼誕生的?

作者:hockeyli,騰訊 PCG 客戶端開發工程師

一、 APK 組成解析

在開始解析 Android 構建流程之前,我們先來看下構建的最終產物 APK 的整體組成:

APK 主要由五個部分組成,分別是:

1.1 Apk 分析工具

工欲善其事,必先利其器,既然想分析 APK 必然少不了好用的工具。

① Android Studio 自帶的 APK 分析器

通過 APK 分析器,我們可以完成這些操作:

② ClassyShark 可以做爲 AS 自帶 APK 分析器的補充,幫我們分析 dex 中的詳細數據,以及查看 APK 中的總方法數以及各個模塊的方法數分佈。

1.2 Dex 知識點拓展

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 數量

二、 構建源碼導讀

2.1 源碼引入

compileOnly "com.android.tools.build:gradle:3.0.1"

編譯完之後,可以在 ApplicationTaskManager#createTasksForVariantScope 中找到創建這些 Task 相關的代碼,也就意味着順藤摸瓜找到這些 Task 的真正實現邏輯。

2.2 BuildConfig Task 詳解

這裏以 BuildConfig 文件的生成爲例,來梳理下如何查看某個 task 的代碼邏輯。

生成 BuildConfig 文件,是通過 ApplicationTaskManager 中通過 createBuildConfigTask 來創建對應的 task。

順着代碼邏輯,我們找到最終真正實現這個邏輯的是:GenerateBuildConfig 這個 task,GenerateBuildConfig 是繼承自 BaseTask,這裏有個小技巧是,Task 中真正的執行邏輯都是在帶着 @TaskAction 註解的方法上的,所以我們能很快找到對應的 generate() 方法。可以看到生成 BuildConfig 整體的邏輯還是比較簡單的,其實就是將 build.gradle 中自帶的屬性以及我們自定義的屬性進行讀取,然後通過 JavaWriter 生成對應的 BuildConfig 文件。

2.3 獲取所有 task 對應的類名

看到上面的例子,可能有些人會拋出一個疑問就是那我們怎麼確定構建中執行的 task 具體對應哪個類呢,這裏提供一個小技巧,其實我們可以在 taskGraph 構建完成之後,將所有 task name 以及對應的 class 進行打印。例如在 build.gradle 中加入這個代碼之後,我們在運行的時候,就會把 task 所對應的類名也都一起打印出來。

三、構建流程梳理

可以看到 Android 構建中會涉及到多個工具,我們可以通過 open $ANDROID_HOME/build-tools 來查看相關的構建工具。

四、手動構建 APK

最後我們通過命令行來手動打包一個可執行的 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
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/214uq3G2qnDpiVZE5w5UkA