【Android 修炼手册】Gradle 篇 -- Android Gradle Plugin 主要 Task 分析
上文回顧
上篇文章里講了 android gradle plugin 的整體流程,引入插件以后生成了很多 Task,這篇文章就談?wù)勆傻倪@些 Task 都有什么用處,以及一些主要 Task 的實現(xiàn)
預(yù)備知識
看完本文可以達到什么程度
閱讀前準備工作
1.項目添加 android gradle plugin 依賴
compile 'com.android.tools.build:gradle:3.0.1' 復(fù)制代碼通過這種方式,可以直接依賴 plugin 的源碼,讀起來比較方便
2.官方對照源碼地址 android gradle plugin 源碼地址
大家可以直接 clone EasyGradle 項目,把 app/build.gradle 里的 implementation 'com.android.tools.build:gradle:3.0.1' 注釋打開就可以了。
在 Gradle的基本使用 和 Android Gradle Plugin 主要流程分析 里,我們知道了 gradle 中 task 的重要性,以及 android gradle plugin 的主要流程,這一篇就來分析一下 android gradle plugin 中一些重要的 task 是怎么執(zhí)行的。
一、Android 打包流程
在介紹 Android Gradle Plugin Task 之前,我們先看看一個 apk 的構(gòu)建流程,先放一張官方流程圖:
官方介紹的流程如下:
那么以 Task 的維度來看 apk 的打包,是什么流程呢?我們先執(zhí)行下面的命令,看一下打包一個 apk 需要哪些 task
首先我們看一下 打包一個 apk 需要哪些 task。 在項目根目錄下執(zhí)行命令
./gradlew android-gradle-plugin-source:assembleDebug --console=plain 復(fù)制代碼看一下輸出結(jié)果
:android-gradle-plugin-source:preBuild UP-TO-DATE :android-gradle-plugin-source:preDebugBuild :android-gradle-plugin-source:compileDebugAidl :android-gradle-plugin-source:compileDebugRenderscript :android-gradle-plugin-source:checkDebugManifest :android-gradle-plugin-source:generateDebugBuildConfig :android-gradle-plugin-source:prepareLintJar UP-TO-DATE :android-gradle-plugin-source:generateDebugResValues :android-gradle-plugin-source:generateDebugResources :android-gradle-plugin-source:mergeDebugResources :android-gradle-plugin-source:createDebugCompatibleScreenManifests :android-gradle-plugin-source:processDebugManifest :android-gradle-plugin-source:splitsDiscoveryTaskDebug :android-gradle-plugin-source:processDebugResources :android-gradle-plugin-source:generateDebugSources :android-gradle-plugin-source:javaPreCompileDebug :android-gradle-plugin-source:compileDebugJavaWithJavac :android-gradle-plugin-source:compileDebugNdk NO-SOURCE :android-gradle-plugin-source:compileDebugSources :android-gradle-plugin-source:mergeDebugShaders :android-gradle-plugin-source:compileDebugShaders :android-gradle-plugin-source:generateDebugAssets :android-gradle-plugin-source:mergeDebugAssets :android-gradle-plugin-source:transformClassesWithDexBuilderForDebug :android-gradle-plugin-source:transformDexArchiveWithExternalLibsDexMergerForDebug :android-gradle-plugin-source:transformDexArchiveWithDexMergerForDebug :android-gradle-plugin-source:mergeDebugJniLibFolders :android-gradle-plugin-source:transformNativeLibsWithMergeJniLibsForDebug :android-gradle-plugin-source:transformNativeLibsWithStripDebugSymbolForDebug :android-gradle-plugin-source:processDebugJavaRes NO-SOURCE :android-gradle-plugin-source:transformResourcesWithMergeJavaResForDebug :android-gradle-plugin-source:validateSigningDebug :android-gradle-plugin-source:packageDebug :android-gradle-plugin-source:assembleDebug 復(fù)制代碼上面就是打包一個 apk 需要的 task
二、Task 對應(yīng)實現(xiàn)類
我們先看看每個 task 都是做什么的,以及其對應(yīng)的實現(xiàn)類。
先回憶一下,我們在前面 android-gradle-plugin 主要流程分析里說到過,task 的實現(xiàn)可以在 TaskManager 里找到,創(chuàng)建 task 的方法主要是兩個,TaskManager.createTasksBeforeEvaluate() 和 ApplicationTaskManager.createTasksForVariantScope(),所以這些 task 的實現(xiàn),也在這兩個類里找就可以,下面列出了各個 task 的作用及實現(xiàn)類。
| preBuild | 空 task,只做錨點使用 | |
| preDebugBuild | 空 task,只做錨點使用,與 preBuild 區(qū)別是這個 task 是 variant 的錨點 | |
| compileDebugAidl | AidlCompile | 處理 aidl |
| compileDebugRenderscript | RenderscriptCompile | 處理 renderscript |
| checkDebugManifest | CheckManifest | 檢測 manifest 是否存在 |
| generateDebugBuildConfig | GenerateBuildConfig | 生成 BuildConfig.java |
| prepareLintJar | PrepareLintJar | 拷貝 lint jar 包到指定位置 |
| generateDebugResValues | GenerateResValues | 生成 resvalues,generated.xml |
| generateDebugResources | 空 task,錨點 | |
| mergeDebugResources | MergeResources | 合并資源文件 |
| createDebugCompatibleScreenManifests | CompatibleScreensManifest | manifest 文件中生成 compatible-screens,指定屏幕適配 |
| processDebugManifest | MergeManifests | 合并 manifest 文件 |
| splitsDiscoveryTaskDebug | SplitsDiscovery | 生成 split-list.json,用于 apk 分包 |
| processDebugResources | ProcessAndroidResources | aapt 打包資源 |
| generateDebugSources | 空 task,錨點 | |
| javaPreCompileDebug | JavaPreCompileTask | 生成 annotationProcessors.json 文件 |
| compileDebugJavaWithJavac | AndroidJavaCompile | 編譯 java 文件 |
| compileDebugNdk | NdkCompile | 編譯 ndk |
| compileDebugSources | 空 task,錨點使用 | |
| mergeDebugShaders | MergeSourceSetFolders | 合并 shader 文件 |
| compileDebugShaders | ShaderCompile | 編譯 shaders |
| generateDebugAssets | 空 task,錨點 | |
| mergeDebugAssets | MergeSourceSetFolders | 合并 assets 文件 |
| transformClassesWithDexBuilderForDebug | DexArchiveBuilderTransform | class 打包 dex |
| transformDexArchiveWithExternalLibsDexMergerForDebug | ExternalLibsMergerTransform | 打包三方庫的 dex,在 dex 增量的時候就不需要再 merge 了,節(jié)省時間 |
| transformDexArchiveWithDexMergerForDebug | DexMergerTransform | 打包最終的 dex |
| mergeDebugJniLibFolders | MergeSouceSetFolders | 合并 jni lib 文件 |
| transformNativeLibsWithMergeJniLibsForDebug | MergeJavaResourcesTransform | 合并 jnilibs |
| transformNativeLibsWithStripDebugSymbolForDebug | StripDebugSymbolTransform | 去掉 native lib 里的 debug 符號 |
| processDebugJavaRes | ProcessJavaResConfigAction | 處理 java res |
| transformResourcesWithMergeJavaResForDebug | MergeJavaResourcesTransform | 合并 java res |
| validateSigningDebug | ValidateSigningTask | 驗證簽名 |
| packageDebug | PackageApplication | 打包 apk |
| assembleDebug | 空 task,錨點 |
三、如何去讀 Task 的代碼
在 gradle plugin 中的 Task 主要有三種,一種是普通的 task,一種是增量 task,一種是 transform,下面分別看下這三種 task 怎么去讀。
如何讀 Task 的代碼
如何讀 IncrementalTask
我們先看看下這個類,這個類表示的是增量 Task,什么是增量呢?是相對于 全量來說的,全量我們可以理解為調(diào)用 clean 以后第一次編譯的過程,這個就是全量編譯,之后修改了代碼或者資源文件,再次編譯,就是增量編譯。
其中比較重要的幾個方法如下:
簡單介紹了 IncrementalTask 之后,我們這里強調(diào)一下,如何去讀一個 增量 Task 的代碼,主要有四步:
如何讀 Transform
四、重點 Task 實現(xiàn)分析
上面每個 task 已經(jīng)簡單說明了具體做什么以及對應(yīng)的實現(xiàn)類,下面選了幾個比較重要的來分析一下其實現(xiàn)
為什么分析這幾個呢?這幾個代表了 gradle 自動生成代碼,資源的處理,以及 dex 的處理,算是 apk 打包過程中比較重要的幾環(huán)。
generateDebugBuildConfig
processDebugManifest
mergeDebugResources
processDebugResources
transformClassesWithDexBuilderForDebug
transformDexArchiveWithExternalLibsDexMergerForDebug
transformDexArchiveWithDexMergerForDebug
分析過程主要下面幾個步驟,實現(xiàn)類,整體實現(xiàn)圖,調(diào)用鏈路(方便以后回看代碼),以及重要代碼分析
4.1 generateDebugBuildConfig
4.1.1 實現(xiàn)類
GenerateBuildConfig
4.1.2 整體實現(xiàn)圖
4.1.3 代碼調(diào)用鏈路
GenerateBuildConfig.generate -> BuildConfigGenerator.generate -> JavaWriter 復(fù)制代碼4.1.4 主要代碼分析
在 GenerateBuildConfig 中,主要生成代碼的步驟如下:
4.2 mergeDebugResources
4.2.1 實現(xiàn)類
MergeResources
4.2.2 整體實現(xiàn)圖
4.2.3 調(diào)用鏈路
MergeResources.doFullTaskAction -> ResourceMerger.mergeData -> MergedResourceWriter.end -> QueueableAapt2.compile -> Aapt2QueuedResourceProcessor.compile -> AaptProcess.compile -> AaptV2CommandBuilder.makeCompile 復(fù)制代碼4.2.4 主要代碼分析
MergeResources 這個類,繼承自 IncrementalTask,按照前面說的閱讀增量 Task 代碼的步驟,依次看三個方法的實現(xiàn):isIncremental,doFullTaskAction,doIncrementalTaskAction
- isIncremental
- doFullTaskAction
這一步調(diào)用 aapt2 命令去處理資源,處理完以后 xxx.xml.flat 格式
- doIncrementalTaskAction
增量任務(wù)過程和全量其實差異不大,只不過是在獲取 resourceSets 的時候,使用的是修改后的文件
4.3 processDebugResources
4.3.1 實現(xiàn)類
ProcessAndroidResources
4.3.2 整體實現(xiàn)圖
4.3.3 調(diào)用鏈路
ProcessAndroidResources.doFullTaskAction -> ProcessAndroidResources.invokeAaptForSplit -> AndroidBuilder.processResources -> QueueAapt2.link -> Aapt2QueuedResourceProcessor.link -> AaptProcess.link -> AaptV2CommandBuilder.makeLink 復(fù)制代碼4.3.4 主要代碼分析
ProcessAndroidResources 也是繼承自 IncrementalTask,但是沒有重寫 isIncremental,所以不是增量的 Task,直接看 doFullTaskAction 即可
- doFullTaskAction
這個里面代碼雖然多,但是主要的邏輯比較簡單,就是調(diào)用 aapt2 link 去生成資源包。
這里會處理 splits apk 相關(guān)的內(nèi)容,關(guān)于 splits apk 具體可以查看 splits apk,簡單來說,就是可以按照屏幕分辨率,abis 來生成不同的 apk,從而讓特定用戶的安裝包變小。
分下面幾個步驟:
返回的是一個 ApkData 列表,ApkData 有三個子類,分別是 Main,Universal,FullSplit
我們配置 如下:
這里的 ApkData 會返回一個 Universal 和多個 FullSplit,Universal 代表的是主 apk,FullSplit 就是根據(jù)屏幕密度拆分的 apk。
如果我們沒有配置 splits apk,那么這里只會返回一個 Main 的實例,標識完整的 apk。
2. 先處理 main 和 不依賴 density 的 ApkData 資源
關(guān)于 aapt2 的 compile 和 link 參數(shù),可以在 developer.android.com/studio/comm… 這里看
4.4 processDebugManifest
4.4.1 實現(xiàn)類
MergeManifests
4.4.2 整體實現(xiàn)圖
4.4.3 調(diào)用鏈路
MergeManifests.dofFullTaskAction -> AndroidBuilder.mergeManifestsForApplication -> Invoker.merge -> ManifestMerge2.merge 復(fù)制代碼4.4.4 主要代碼分析
MergeManifests 也是繼承了 IncrementalTask,但是沒有實現(xiàn) isIncremental,所以只看其 doFullTaskAction 即可。
這個 task 功能主要是合并 mainfest,包括 module 和 flavor 里的,整個過程通過 MergingReport,ManifestMerger2 和 XmlDocument 進行。
這里直接看 ManifestMerger2.merge() 的 merge 過程 。 主要有幾個步驟:
4.5 transformClassesWithDexBuilderForDebug
4.5.1 實現(xiàn)類
DexArchiveBuilderTransform
4.5.2 整體實現(xiàn)圖
4.5.3 調(diào)用鏈路
DexArchiveBuilderTransform.transform -> DexArchiveBuilderTransform.convertJarToDexArchive -> DexArchiveBuilderTransform.convertToDexArchive -> DexArchiveBuilderTransform.launchProcessing -> DxDexArchiveBuilder.convert 復(fù)制代碼4.5.4 主要代碼分析
在 DexArchiveBuilderTransform 中,對 class 的處理分為兩種方式,一種是對 目錄下的 class 進行處理,一種是對 .jar 里的 class 進行處理。
為什么要分為這兩種方式呢?.jar 中的 class 一般來說都是依賴庫,基本上不會改變,gradle 在這里做了一個緩存,但是兩種方式最終都會調(diào)用到 convertToDexArchive,可以說是殊途同歸吧。
- convertJarToDexArchive 處理 jar
處理 .jar 時,會對 jar 包中的每一個 class 都單獨打成一個 .dex 文件,之后還是放在 .jar 包中
- convertToDexArchive 處理 dir 以及 jar 的后續(xù)處理
對 dir 處理使用 convertToDexArchive
其中會調(diào)用 launchProcessing
在 launchProcessing 中,有下面幾個步驟:
4.6 transformDexArchiveWithExternalLibsDexMergerForDebug
4.6.1 實現(xiàn)類
ExternalLibsMergerTransform
4.6.2 整體實現(xiàn)圖
4.6.3 調(diào)用鏈路
這一步是處理依賴庫的 dex,把上一步生成的依賴庫的 dex merge 成一個 dex
// dx ExternalLibsMergerTransform.transform -> DexMergerTransformCallable.call -> DxDexArchiveMerger.mergeDexArchives -> DxDexArchiveMerger.mergeMonoDex -> DexArchiveMergerCallable.call -> DexMerger.merge 復(fù)制代碼// d8 ExternalLibsMergerTransform.transform -> DexMergerTransformCallable.call -> D8DexArchiveMerger.mergeDexArchives -> 調(diào)用 D8 命令 復(fù)制代碼這里邏輯比較簡單,就不具體分析了
4.7 transformDexArchiveWithDexMergerForDebug
4.7.1 實現(xiàn)類
DexMergerTransform
4.7.2 整體實現(xiàn)圖
4.7.3 調(diào)用鏈路
和上一步類似
// dx DexMergerTransform.transform -> DexMergerTransform.handleLegacyAndMonoDex -> DexMergerTransformCallable.call -> DxDexArchiveMerger.mergeDexArchives -> DxDexArchiveMerger.mergeMonoDex -> DexArchiveMergerCallable.call -> DexMerger.merge 復(fù)制代碼// d8 DexMergerTransform.transform -> DexMergerTransform.handleLegacyAndMonoDex -> DexMergerTransformCallable.call -> D8DexArchiveMerger.mergeDexArchives -> 調(diào)用 D8 命令 復(fù)制代碼五、本文重點
【Android 修煉手冊】系列內(nèi)容 每周更新
歡迎關(guān)注下面賬號,獲取更新:
微信搜索公眾號: ZYLAB
Github
掘金
總結(jié)
以上是生活随笔為你收集整理的【Android 修炼手册】Gradle 篇 -- Android Gradle Plugin 主要 Task 分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三星集团和华为集团,哪个更厉害?
- 下一篇: 如果信用卡欠款不还被坐牢,那所欠的钱还用