當(dāng)前位置:首頁 > IT技術(shù) > 移動平臺 > 正文

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程
2021-09-24 14:55:04

??Android Gradle Plugin??,簡稱 AGP,老早之前就想好好研究下Android APK的打包過程,畢竟 ??APK包體積優(yōu)化?? 的前置知識之一。

奈何當(dāng)時的知識儲備嚴(yán)重不足,硬啃著實(shí)難受,在學(xué)了兩周的Gradle后 ,覺得應(yīng)該有 一戰(zhàn)之力 了,所以這一節(jié)來了!

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_java

內(nèi)容較多,建議先收藏,有時間再慢慢細(xì)品~

0x1、網(wǎng)上流傳的三張APK打包流程圖

??Android官網(wǎng)?? 有一張新的打包流程圖(左),相比起舊的流程圖(右)更抽象,隱藏了很多細(xì)節(jié):

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_java_02

在 ??Android Studio Project Site?? 還找到了一張更詳細(xì)的圖:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_android_03

如果只是滿足于一個寫基礎(chǔ)業(yè)務(wù)的Android開發(fā)仔,了解下足矣,不過如果想有更深的造詣,還是建議往下學(xué)的。

比如面試時(我自己腦補(bǔ)的~):

  • 面試官:簡歷上寫的做過APK體積優(yōu)化?說說都做了哪方面的優(yōu)化:
  • 你:從資源大頭入手,把png圖片都轉(zhuǎn)成webp,使用AndResGuard對資源進(jìn)行混淆;
  • 面試官:用Gradle寫一個webp轉(zhuǎn)換插件,說下思路;
  • 你:不會,我一般右鍵直接轉(zhuǎn)換…
  • 面試官:那說下AndResGuard的大概原理;
  • 你:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_android_04

本節(jié)就來了解下AGP的構(gòu)建過程,以及簡單了解下APK的打包過程~

Tips:Tasks那么多,不可能一個個去精讀源碼解析,不同插件版本還有差異,不如授之以漁,本文的目的就是讓讀者遇到問題時懂得如何追根溯源,找到對應(yīng)的源碼。



0x2、如何查看插件源碼

研究對象是AGP的源碼,所以要先搞一份源碼,方法有下述幾種:

1. 下載完整源碼

如果磁盤空間比較充足,可以通過repo的方式,將Android Gradle Plugin的源碼下載到本地(貌似30多G):

# 最新源碼的只有3.4.0的
repo init -u https://android.googlesource.com/platform/manifest -b gradle_3.4.0
repo sync

2. 下載部分源碼

當(dāng)然不需要編譯的話,可以直接下對應(yīng)源碼包,來到下述地址:??build-system??

點(diǎn)tgz下載:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_05

然后用VS Code之類的代碼查看工具查看即可~

3. 取巧(推薦)

在app層級的build.gradle添加下述依賴:

implementation 'com.android.tools.build:gradle:3.4.0'

build下,然后在左側(cè) ??External Libraries?? 即可找到源碼:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_06



0x3、閱讀源碼前的一些補(bǔ)充

閱讀源碼前建議溫習(xí)下我前面寫的三篇文章,另外補(bǔ)充點(diǎn)姿勢:

Gradle Plugin 中的Task主要有三種:??普通Task??、??增量Task??、??Transform??。

Task一般會繼承 ??DefaultTask?? 或 ??IncrementalTask??,而 ??@TaskAction?? 注解的方法,就是此Task做的事。

繼承 ??IncrementalTask?? 的類為增量Task,這個增量是相對于全量來說的,全量指的是:調(diào)用完clean后第一次編譯過程,修改代碼或資源后再次編譯,就是增量編譯。幾個關(guān)鍵方法:

public abstract class IncrementalTask extends BaseTask {
// 是否需要增量,默認(rèn)false
@Internal protected boolean isIncremental() { }

// 需要子類實(shí)現(xiàn),全量時執(zhí)行的任務(wù)
protected abstract void doFullTaskAction() throws Exception;

// 增量時執(zhí)行的任務(wù),默認(rèn)什么都不執(zhí)行,參數(shù)是增量時修改過的文件
protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws Exception{ }

@TaskAction
void taskAction(IncrementalTaskInputs inputs) throws Exception {
// 判斷是否是增量,是執(zhí)行doIncrementalTaskAction,否則執(zhí)行doFullTaskAction


// 獲取修改文件
private Map<File, FileStatus> getChangedInputs(IncrementalTaskInputs inputs) { }
}

至于 ??Transform??(變換),是Android官方提供給開發(fā)者,在**.class → .dex轉(zhuǎn)換期間用來修改.class文件的一套API**,留意 ??transform()?? 方法的實(shí)現(xiàn)就好。



0x4、執(zhí)行g(shù)radle assemble的Task鏈

我們常常使用下面的命令來打包APK:

gradlew assemble

可以由此入手,看下打包一次都涉及到了哪些Task,鍵入下述命令(linux、mac使用./gradlew):

gradlew assemble --console=plain

輸出結(jié)果及要點(diǎn)簡述如下所示:

:app:preBuild UP-TO-DATE    → 空task,錨點(diǎn)
:app:preDebugBuild → 空task,錨點(diǎn)
:app:compileDebugAidl NO-SOURCE → 處理AIDL
:app:checkDebugManifest → 檢查Manifest是否存在
:app:compileDebugRenderscript NO-SOURCE → 處理renderscript
:app:generateDebugBuildConfig → 生成 BuildConfig.java
:app:mainApkListPersistenceDebug → 生成 app-list.gson
:app:generateDebugResValues → 生成resvalue,generated.xml
:app:generateDebugResources → 空task,錨點(diǎn)
:app:mergeDebugResources → 合并資源文件
:app:createDebugCompatibleScreenManifests → manifest文件中生成compatible-screens,指定屏幕適配
:app:processDebugManifest → 合并manifest.xml文件
:app:processDebugResources → aapt打包資源
:app:compileDebugKotlin → 編譯Kotlin文件
:app:prepareLintJar UP-TO-DATE → 拷貝 lint jar包到指定位置
:app:generateDebugSources → 空task,錨點(diǎn)
:app:javaPreCompileDebug → 生成 annotationProcessors.json 文件
:app:compileDebugJavaWithJavac → 編譯 java文件
:app:compileDebugNdk → 編譯ndk
:app:compileDebugSources → 空task,錨點(diǎn)
:app:mergeDebugShaders → 合并 shader文件
:app:compileDebugShaders → 編譯 shaders
:app:generateDebugAssets → 空task,錨點(diǎn)
:app:mergeDebugAssets → 合并 assests文件
:app:validateSigningDebug → 驗(yàn)證簽名
:app:signingConfigWriterDebug → 編寫SigningConfig信息
:app:checkDebugDuplicateClasses → 檢查重復(fù)class
:app:transformClassesWithDexBuilderForDebug → class打包成dex
:app:transformDexArchiveWithExternalLibsDexMergerForDebug → 打包第三方庫的dex
:app:transformDexArchiveWithDexMergerForDebug → 打包最終的dex
:app:mergeDebugJniLibFolders → 合并jni lib 文件
:app:transformNativeLibsWithMergeJniLibsForDebug → 合并jnilibs
:app:transformNativeLibsWithStripDebugSymbolForDebug → 去掉native lib里的debug符號
:app:processDebugJavaRes NO-SOURCE → 處理java res
:app:transformResourcesWithMergeJavaResForDebug → 合并java res
:app:packageDebug → 打包apk
:app:assembleDebug → 空task,錨點(diǎn)
:app:extractProguardFiles → 生成混淆文件

# 還會打一個release包,task和上述基本一致,此處省略~

當(dāng)然,也可以直接在 ??Build?? 窗口直接查看,雙擊右側(cè)Gradle窗口中assemble的Task,然后觀察此窗口:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_jvm_07

嘖嘖,還可以看到每個Task的執(zhí)行時間,不錯,但先不跟每個Task具體內(nèi)容,而是跟下AGP的構(gòu)建過程~

0x5、AGP的構(gòu)建過程

上一節(jié)將Gradle插件時說過,每個插件都會配置一個 ??id名字.properties?? 的文件,在此寫上插件的實(shí)現(xiàn)類,全局搜定位到下述文件:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_08

打開:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_09

指向:??AppPlugin?? 類,跟下:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_10

上節(jié)說過:插件類都繼承于 ??Plugin??,入口函數(shù) ??apply()??,但在這里沒找到,跟下:??AbstractAppPlugin?? → ??BasePlugin??。

① BasePlugin

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_android_11

行吧,在BasePlugin中重寫了 ??apply()?? 方法,里面調(diào)用了兩個函數(shù),先跟下:??basePluginApply()?? 補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_12

執(zhí)行一些檢查操作,接著是 插件的初始化及配置,而另一個函數(shù):??pluginSpecificApply()?? 則是空實(shí)現(xiàn),接著跟下:配置項(xiàng)目、配置擴(kuò)展及創(chuàng)建Tasks的過程。

② configureProject() → 配置項(xiàng)目

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_android_13

創(chuàng)建DataBindingBuilder實(shí)例,強(qiáng)制使用不低于當(dāng)前所支持的最小插件版本,應(yīng)用Java插件,如果啟用了構(gòu)建緩存選項(xiàng),創(chuàng)建buildCache實(shí)例,添加了一個回調(diào):所有project執(zhí)行完后執(zhí)行資源回收相關(guān)操作。

③ configureExtension() → 配置DSL擴(kuò)展

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_14

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_15

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_android_16

完成下述幾項(xiàng)工作:

  • ① 創(chuàng)建build.gradle中的Android DSL;
  • ② 創(chuàng)建VariantFactory、TaskManager、VariantManager實(shí)例;
  • ③ 注冊新增/移除配置的回調(diào),包括:signingConfig,buildType,productFlavor;
  • ④ 創(chuàng)建默認(rèn)的debug簽名、debug和release兩種buildType;

④ createTasks() → 創(chuàng)建Tasks

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_17

跟下 ??createAndroidTask()??:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_18

跟下 ??createAndroidTasks()??:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_java_19

注意下:這里遍歷了所有的variantScope,然后調(diào)用 ??createTasksForVariantData()?? 創(chuàng)建變體數(shù)據(jù)對應(yīng)的Tasks:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_20

跟下:??createTasksForVariantScope()??:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_21

抽象方法,看下哪里實(shí)現(xiàn)了這個方法,搜下:??extends TaskManager??

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_22

最終定位到了:??ApplicationTaskManager?? 類

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_android_23

噢吼,就是在這里完成APK打包過程的Tasks,可以簡單跟跟驗(yàn)證下:??createAnchorTasks()??,創(chuàng)建錨點(diǎn)Tasks:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_24

跟下:??createVariantPreBuildTask()??

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_java_25

2333,跟上面的APK打包Task鏈的相呼應(yīng),AGP插件的構(gòu)建過程就跟到這里,接著了解下APK打包的Task。

0x6、Apk的打包過程

Tips:分享下搜索Task的實(shí)現(xiàn)類的技巧 → 全局搜 “xxx”, “yyy” 即可快速定位對應(yīng)Task類,如 “compile”, “Aidl”,或者搜索整個Task,然后刪刪刪匹配。

1. compileDebugAidl

過程簡述:??將.aidl文件通過aidl工具轉(zhuǎn)換成編譯器能夠處理的Java接口文件?? 相關(guān)代碼:AidlCompile.java → AidlProcessor.java → call()



2. checkDebugManifest

過程簡述:??檢查AndroidManifest.xml文件是否存在?? 相關(guān)代碼:CheckManifest.java



3. compileDebugRenderscript

過程簡述:??處理Renderscript文件(.rs)?? 相關(guān)代碼:RenderscriptCompile.java



4. generateDebugBuildConfig

過程簡述:??生成 BuildConfig.java 文件?? 相關(guān)代碼:GenerateBuildConfig.java



5. mainApkListPersistenceDebug

過程簡述:??持久化APK數(shù)據(jù)到apk-list.gson中?? 相關(guān)代碼:MainApkListPersistence.kt



6. generateDebugResValues

過程簡述:??遍歷res下的values目錄下xml文件,生成resValues文件generated.xml?? 相關(guān)代碼:GenerateResValues.java → generate() → ResValueGenerator.java



7. mergeDebugResources

過程簡述:??使用AAPT2合并資源文件?? 相關(guān)代碼:MergeResources.doFullTaskAction() → ResourceMerger.mergeData() → MergedResourceWriter.end() → mResourceCompiler.submitCompile() → AaptV2CommandBuilder.makeCompileCommand()

核心源碼解析:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_java_26

實(shí)現(xiàn)了isIncremental()方法,返回true,說明支持增量編譯,跟下全量編譯方法 ??doFullTaskAction()??

ResourcePreprocessor preprocessor = getPreprocessor();
List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor)

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_jvm_27

接著往下走:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_28

繼續(xù):

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_29

點(diǎn)進(jìn)merger.mergeData() → ResourceMerger.mergeData() → DataMerger.mergeData()

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_jvm_30

嘔吼,實(shí)際上調(diào)用的還是 ??MergedResourceWriter?? 類里的方法,跟下addItem():

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_android_31

不同文件會創(chuàng)建對應(yīng)的 ??CompileResourceRequest?? 實(shí)例,并添加到 ??mCompileResourceRequests?? 中,后者是一個ConcurrentLinkedQueue隊(duì)列,資源最后會在end()方法處處理:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_32

最終調(diào)用 ??AaptV2CommandBuilder.makeCompileCommand()?? 方法生成aapt2命令去處理資源。

Tips:將圖片轉(zhuǎn)為webp格式的插件一般在此Task前處理~



8. createDebugCompatibleScreenManifests

過程簡述:??manifest文件中生成compatible-screens,用于屏幕適配?? 相關(guān)代碼:CompatibleScreensManifest.kt



9. processDebugManifest

過程簡述:??合并AndroidManifest.xml文件?? 相關(guān)代碼:ProcessApplicationManifest.java、ProcessLibraryManifest.java



10. processDebugResources

過程簡述:??調(diào)用aapt2 link 打包資源并生成R.java文件?? 相關(guān)代碼:TaskManager.java → createProcessResTask()



11. compileDebugKotlin

過程簡述:??編譯Kotlin文件為字節(jié)碼?? 相關(guān)代碼:沒找到…可能在kotlin插件源碼里



12. prepareLintJar

過程簡述:??拷貝lint jar包到指定位置?? 相關(guān)代碼:PrepareLintJar.java



13. avaPreCompileDebug

過程簡述:??生成annotationProcessors.json文件?? 相關(guān)代碼:JavaPreCompileTask.java



14. ompileDebugJavaWithJavac

過程簡述:??編譯java文件?? 相關(guān)代碼:AndroidJavaCompile.java



15. compileDebugNdk

過程簡述:??編譯NDK?? 相關(guān)代碼:NdkCompile.java



15. mergeDebugShaders

過程簡述:??合并Renderscript文件(.rs)?? 相關(guān)代碼:MergeSourceSetFolders.java



16. compileDebugShaders

過程簡述:??編譯Renderscript文件(.rs)?? 相關(guān)代碼:ShaderCompile.java



17. mergeDebugAssets

過程簡述:??合并assets文件?? 相關(guān)代碼:MergeSourceSetFolders.java



18. validateSigningDebug

過程簡述:??驗(yàn)證簽名?? 相關(guān)代碼:ValidateSigningTask.kt 附加信息:檢查當(dāng)前Variant的簽名配置中是否存在密鑰庫文件,如果當(dāng)前密鑰庫默認(rèn)為debug keystore,那密鑰庫不存在也會進(jìn)行相應(yīng)的創(chuàng)建。



19. signingConfigWriterDebug

過程簡述:??編寫SigningConfig信息?? 相關(guān)代碼:SigningConfigWriterTask.kt



20. checkDebugDuplicateClasses

過程簡述:??檢查重復(fù)class?? 相關(guān)代碼:CheckDuplicateClassesTask.kt 附加信息:檢查項(xiàng)目外部依賴是否不包含重復(fù)類,打包成dex的時候再檢測報(bào)錯不怎么友好,所以引入了這個Task用于快速失敗。



21. transformClassesWithDexBuilderForDebug

過程簡述:??將class打包成dex?? 相關(guān)代碼:DexArchiveBuilderTransform.java

核心代碼解析:

定位到 ??transform()?? 方法,可以看到對class的處理分為了兩種,目錄下的 class和.jar里的class:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_33

跟下 ??processJarInput()??:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_34

繼續(xù)跟:??convertJarToDexArchive()??

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_35

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_java_36

對class兩種處理方式,最后都走到 ??convertToDexArchive()??,其中調(diào)用了 ??launchProcessing()??:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_jvm_37

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_java_38

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_android_39

這里的 ??dexArchiveBuilder.convert()?? 其實(shí)就是內(nèi)部調(diào)用dx或d8來打dex,跟下賦值處:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_android_40



22. transformDexArchiveWithExternalLibsDexMergerForDebug

過程簡述:??打包第三方庫的dex?? 相關(guān)代碼:ExternalLibsMergerTransform.kt 核心代碼解析:

同樣跟 ??transform()??:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_41

創(chuàng)建了一個 ??DexMergerTransformCallable?? 實(shí)例,然后調(diào) ??call()?? 方法:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_jvm_42

比較簡單,就是調(diào)下dx或d8將上面生成的依賴庫的dex合并成一個dex。



23. transformDexArchiveWithDexMergerForDebug

過程簡述:??打包最終的dex?? 相關(guān)代碼:DexMergerTransform.transform() → mergeDex() 核心代碼解析:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_43

跟下 ??submitForMerging()?? :

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_44

也是創(chuàng)建了一個 ??DexMergerTransformCallable?? 實(shí)例,剩余邏輯同上~



24. mergeDebugJniLibFolders

過程簡述:??合并jni lib文件?? 相關(guān)代碼:MergeSourceSetFolders.java



25. transformNativeLibsWithMergeJniLibsForDebug

過程簡述:??合并jnilibs?? 相關(guān)代碼:MergeJavaResourcesTransform.java



26. transformNativeLibsWithStripDebugSymbolForDebug

過程簡述:??去掉native lib里的debug符號?? 相關(guān)代碼:StripDebugSymbolTransform.java



27. processDebugJavaRes

過程簡述:??處理java res?? 相關(guān)代碼:MergeJavaResourcesTransform.java



28. transformResourcesWithMergeJavaResForDebug

過程簡述:??合并java res?? 相關(guān)代碼:MergeJavaResourcesTransform.java



29. packageDebug

過程簡述:??打包APK?? 相關(guān)代碼:PackageApplication.java → PackageAndroidArtifact.doTask()

核心代碼如下:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_45

而上面的這些updateXxx()方法,調(diào)用的都是:??IncrementalPackager → updateFiles()??

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_java_46

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_gradle_47

最終調(diào)用mApkCreator.writeZip將上述內(nèi)容寫入到APK中。



30. extractProguardFiles

過程簡述:??生成混淆文件?? 相關(guān)代碼:ExtractProguardFiles.java



補(bǔ)充:錨點(diǎn)Task → 空Task

上面的Tasks過濾了錨點(diǎn)Task,啥事錨點(diǎn)Task?答:空Task,用來表明處于某種狀態(tài)

以 ??preBuild?? 為例,全局搜它,定位到: ??TaskManager → MAIN_PREBUILD??:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_移動開發(fā)_48

跟下引用處:??createTasksBeforeEvaluate()??:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_java_49

注冊了一個名為 **??MAIN_PREBUILD??**的Task,但沒有傳閉包(任務(wù)內(nèi)容),即空Task。



小結(jié)

以上就是本節(jié)的全部內(nèi)容,看完好像懂了些什么,又說不出來懂了什么,沒關(guān)系,畢竟有點(diǎn)偏理論,為后面Gradle的更深入學(xué)習(xí)及應(yīng)用做鋪墊而已,不急,有疑問或文中有誤地方歡迎評論區(qū)指出,謝謝~

對了,喂,三點(diǎn)幾了:

補(bǔ)齊Android技能樹——從AGP構(gòu)建過程到APK打包過程_java_50

本文摘自 :https://blog.51cto.com/u

開通會員,享受整站包年服務(wù)立即開通 >