Android打包流程Gradle Plugin 主要 Task 分析

Android打包流程Gradle Plugin 主要 Task 分析

一、Android 打包流程

官方流程圖:


1.編譯器將您的源代碼轉(zhuǎn)換成 DEX(Dalvik Executable) 文件(其中包括 Android 設(shè)備上運(yùn)行的字節(jié)碼),將所有其他內(nèi)容轉(zhuǎn)換成已編譯資源。

2.APK 打包器將 DEX 文件和已編譯資源合并成單個(gè) APK。 不過(guò),必須先簽署 APK,才能將應(yīng)用安裝并部署到 Android 設(shè)備上。

3.APK 打包器使用調(diào)試或發(fā)布密鑰庫(kù)簽署您的 APK:

4.在生成最終 APK 之前,打包器會(huì)使用 zipalign 工具對(duì)應(yīng)用進(jìn)行優(yōu)化,減少其在設(shè)備上運(yùn)行時(shí)占用的內(nèi)存。

以?Task 的維度來(lái)看 apk 的打包,則分為以下流程:

```

: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: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

```

這些Task的對(duì)應(yīng)實(shí)現(xiàn)類(lèi)和作用如下表所示:

二、Task簡(jiǎn)單介紹

我們的所有Gradle的構(gòu)建工作都是由Task組合完成的,它可以幫助我們處理很多工作。每次構(gòu)建(build)至少由一個(gè)project構(gòu)成,一個(gè)project由一個(gè)至多個(gè)task構(gòu)成。每個(gè)task代表了構(gòu)建過(guò)程當(dāng)中的一個(gè)原子性操作,比如編譯、打包、發(fā)布等等這些操作。在Gradle環(huán)境下可以通過(guò)命令./gradlew tasks 查看當(dāng)前工程所有的task。

Gradle 為我們提供了很多默認(rèn)的task來(lái)進(jìn)行使用,下面是Gradle 官網(wǎng)文檔上對(duì)copy Task 的使用提供的例子。除了copy,還有delete、upload、zip等許多使用的task。這些基礎(chǔ)task可以用來(lái)解決插件資源復(fù)制等問(wèn)題。

官方網(wǎng)站:https://docs.gradle.org/current/dsl/org.gradle.api.Task.html#N18D13

官網(wǎng)講的很詳細(xì),我這邊簡(jiǎn)要介紹一下。在 gradle plugin 中的 Task 主要有三種,一種是普通的 task,一種是增量 task,一種是 transform。

?1.???Task的創(chuàng)建和配置

Task創(chuàng)建主要是兩種方式:直接使用task函數(shù)創(chuàng)建或者通過(guò)task容器TaskContainer對(duì)象的create()方法進(jìn)行創(chuàng)建。

```

task hello{

}

this.task.create('hello'){

}

```

Task 配置:上面兩種創(chuàng)建方式其實(shí)都有對(duì)應(yīng)的重載方法,可以傳入具體的配置參數(shù)來(lái)進(jìn)行Task初始化配置。

2.Task的執(zhí)行

當(dāng)執(zhí)行一個(gè)Task的時(shí)候,其實(shí)就是執(zhí)行其擁有的actions列表,這個(gè)列表保存在Task對(duì)象實(shí)例中的actions成員變量中,其類(lèi)型就是一個(gè)List。Task本質(zhì)上又是由一組被順序執(zhí)行的Action對(duì)象構(gòu)成,Action其實(shí)是一段代碼塊,類(lèi)似與Java中的方法。這里主要介紹Task創(chuàng)建Action的兩個(gè)方法,doFirst與doLast。doFirst{}?可以使代碼在Gradle 的執(zhí)行階段中Task之前執(zhí)行,而doLast{}則恰恰相反,是在Task之后執(zhí)行。

3.Task的執(zhí)行順序

a.dependsOn 是task 配置參數(shù)之一,主要作用就是為task 添加依賴(lài)task ,保證task 之間的執(zhí)行順序。

A-->B-->C

b.TaskInputs TaskOutputs

TaskInputs:

Task的輸入類(lèi),參數(shù)可以接收為任意對(duì)象以及文件、文件夾。?

TaskOutputs:

TaskOutputs?files ( );

TaskOutputs?file ( );

TaskOutputs?dir ( );

Task的輸出類(lèi),只接收文件類(lèi)型。

c.API指定執(zhí)行順序

Task的shouldRunAfter 方法和mustRunAfter方法可以控制一個(gè)Task應(yīng)該或者一定在某個(gè)Task之后執(zhí)行。通過(guò)這種方式可以在某些情況下控制任務(wù)的執(zhí)行順序,而不是通過(guò)強(qiáng)依賴(lài)的方式。

taskB.shouldRunAfter(taskA)表示taskB應(yīng)該在taskA執(zhí)行之后執(zhí)行,這里并不是強(qiáng)制的,所以有可能任務(wù)順序并不會(huì)按預(yù)設(shè)的執(zhí)行。

taskB.mustRunAfter(taskA)表示taskB必須在taskA執(zhí)行之后執(zhí)行,執(zhí)行任務(wù)的順序是確認(rèn)的。

4.掛接自定義Task到構(gòu)建過(guò)程中

Gradle 的生命周期:

A. 初始化階段(Initialization)

初始化階段gradle會(huì)去解析項(xiàng)目根工程中setting.gradle中的include信息,確定哪些工程加入構(gòu)建。?

B. 配置階段(Configuration)

配置階段將解析所有工程的build.gradle腳本,配置project對(duì)象,創(chuàng)建、配置task等相關(guān)信息。

C. 執(zhí)行階段(Execution)

根據(jù)具體的gradle命令,執(zhí)行對(duì)應(yīng)相關(guān)的task以及其依賴(lài)的task。

而實(shí)際項(xiàng)目中我們將要掛接的正是gradle的執(zhí)行階段,因?yàn)橹挥性谂渲秒A段完成,執(zhí)行階段gradle才會(huì)去執(zhí)行系統(tǒng)默認(rèn)task 以及自定義task 。project為我們提供了這樣的方法project.afterEvaluate{}。

project.afterEvaluate{}?在配置完成后,可以保證獲取到所有的task,包括系統(tǒng)默認(rèn)執(zhí)行的task,這樣就可以將我們自定義的task 通過(guò)順序執(zhí)行指定,掛接到構(gòu)建過(guò)程中。

后面我們有個(gè)例子詳細(xì)解讀。

5.Task的啟用與禁用

Task中有個(gè)enabled屬性,用于啟用和禁用任務(wù),默認(rèn)是true,表示啟用,設(shè)置為false則禁止該任務(wù),在執(zhí)行階段該任務(wù)會(huì)被跳過(guò)。在實(shí)際項(xiàng)目中是很使用的一個(gè)技巧,如提升build編譯速度,禁止一些測(cè)試相關(guān)的Task,從而縮短執(zhí)行時(shí)間。

hello.enabled=false

三、重點(diǎn)Task分析

官方網(wǎng)站:https://android.googlesource.com/platform/tools/base/+/refs/tags/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/GenerateBuildConfig.java

我們把這幾個(gè)重點(diǎn)Task拿出來(lái)分析,因?yàn)檫@幾個(gè)代表了 gradle 自動(dòng)生成代碼,資源的處理,以及 dex 的處理,算是 apk 打包過(guò)程中比較重要的幾環(huán)。

generateDebugBuildConfig ----生成BuildConfig.java

processDebugManifest ----合并Manifest文件

mergeDebugResources ----合并資源文件

processDebugResources ---aapt打包資源

transformClassesWithDexBuilderForDebug ----class打包dex

transformDexArchiveWithExternalLibsDexMergerForDebug ----打包三方庫(kù)的dex

transformDexArchiveWithDexMergerForDebug ----打包最終的dex

3.1 generateDebugBuildConfig

3.1.1 實(shí)現(xiàn)類(lèi)

GenerateBuildConfig

3.1.2 代碼調(diào)用鏈路

GenerateBuildConfig.generate -> BuildConfigGenerator.generate -> JavaWriter

3.1.3 主要代碼分析

在 GenerateBuildConfig 中,主要生成代碼的步驟如下:

生成 BuildConfigGenerator

添加默認(rèn)的屬性,包括 DEBUG,APPLICATION_ID,F(xiàn)LAVOR,VERSION_CODE,VERSION_NAME

添加自定義屬性

調(diào)用 JavaWriter 生成 BuildConfig.java 文件

3.2 mergeDebugResources

3.2.1 實(shí)現(xiàn)類(lèi)

MergeResources

3.2.2 調(diào)用鏈路

MergeResources.doFullTaskAction -> ResourceMerger.mergeData -> MergedResourceWriter.end -> QueueableAapt2.compile -> Aapt2QueuedResourceProcessor.compile -> AaptProcess.compile -> AaptV2CommandBuilder.makeCompile

3.2.3 代碼分析

MergeResources 這個(gè)類(lèi),繼承自 IncrementalTask,按照前面說(shuō)的閱讀增量 Task 代碼的步驟,依次看三個(gè)方法的實(shí)現(xiàn):isIncremental,doFullTaskAction,doIncrementalTaskAction

isIncremental

// 說(shuō)明 Task 支持增量

? ? protected boolean isIncremental() {

? ? ? ? return true;

? ? }

doFullTaskAction

通過(guò) getConfiguredResourceSets() 獲取 resourceSets,包括了自己的 res/ 和 依賴(lài)庫(kù)的 res/ 以及 build/generated/res/rs

// MergeResources.doFullTaskAction()

List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor);

創(chuàng)建 ResourceMerger

// MergeResources.doFullTaskAction()

ResourceMerger merger = new ResourceMerger(minSdk);

創(chuàng)建 QueueableResourceCompiler,因?yàn)?gradle3.x 以后支持了 aapt2,所以這里有兩種選擇 aapt 和 aapt2。其中 aapt2 有三種模式,OutOfProcessAaptV2,AaptV2Jni,QueueableAapt2,這里默認(rèn)創(chuàng)建了 QueueableAapt2,resourceCompiler =QueueableAapt2

3.3 processDebugResources

3.3.1 實(shí)現(xiàn)類(lèi)

ProcessAndroidResources


3.3.2 調(diào)用鏈路

ProcessAndroidResources.doFullTaskAction -> ProcessAndroidResources.invokeAaptForSplit -> AndroidBuilder.processResources -> QueueAapt2.link -> Aapt2QueuedResourceProcessor.link -> AaptProcess.link -> AaptV2CommandBuilder.makeLink

3.3.3 代碼分析

ProcessAndroidResources 也是繼承自 IncrementalTask,但是沒(méi)有重寫(xiě) isIncremental,所以不是增量的 Task,直接看 doFullTaskAction 即可

doFullTaskAction

這個(gè)里面代碼雖然多,但是主要的邏輯比較簡(jiǎn)單,就是調(diào)用 aapt2 link 去生成資源包。

這里會(huì)處理 splits apk 相關(guān)的內(nèi)容,關(guān)于 splits apk 具體可以查看?splits apk,簡(jiǎn)單來(lái)說(shuō),就是可以按照屏幕分辨率,abis 來(lái)生成不同的 apk,從而讓特定用戶(hù)的安裝包變小。

3.4 processDebugManifest

3.4.1 實(shí)現(xiàn)類(lèi)

MergeManifests


3.4.2 調(diào)用鏈路

MergeManifests.dofFullTaskAction -> AndroidBuilder.mergeManifestsForApplication -> Invoker.merge -> ManifestMerge2.merge

3.4.3 代碼分析

MergeManifests 也是繼承了 IncrementalTask,但是沒(méi)有實(shí)現(xiàn) isIncremental,所以只看其 doFullTaskAction 即可。

這個(gè) task 功能主要是合并 mainfest,包括 module 和 flavor 里的,整個(gè)過(guò)程通過(guò) MergingReport,ManifestMerger2 和 XmlDocument 進(jìn)行。

這里直接看 ManifestMerger2.merge() 的 merge 過(guò)程 。 主要有幾個(gè)步驟:

獲取依賴(lài)庫(kù)的 manifest 信息,用 LoadedManifestInfo 標(biāo)識(shí)

獲取主 module 的 manifest 信息,替換主 module 的 Manifest 中定義的某些屬性,替換成 gradle 中定義的屬性 例如: package, version_code, version_name, min_sdk_versin 等等

3.5 transformClassesWithDexBuilderForDebug

3.5.1 實(shí)現(xiàn)類(lèi)

DexArchiveBuilderTransform


3.5.2 調(diào)用鏈路

DexArchiveBuilderTransform.transform -> DexArchiveBuilderTransform.convertJarToDexArchive -> DexArchiveBuilderTransform.convertToDexArchive -> DexArchiveBuilderTransform.launchProcessing -> DxDexArchiveBuilder.convert

3.5.4 主要代碼分析

在 DexArchiveBuilderTransform 中,對(duì) class 的處理分為兩種方式,一種是對(duì) 目錄下的 class 進(jìn)行處理,一種是對(duì) .jar 里的 class 進(jìn)行處理。

為什么要分為這兩種方式呢?.jar 中的 class 一般來(lái)說(shuō)都是依賴(lài)庫(kù),基本上不會(huì)改變,gradle 在這里做了一個(gè)緩存,但是兩種方式最終都會(huì)調(diào)用到 convertToDexArchive,可以說(shuō)是殊途同歸吧。

convertJarToDexArchive 處理 jar

處理 .jar 時(shí),會(huì)對(duì) jar 包中的每一個(gè) class 都單獨(dú)打成一個(gè) .dex 文件,之后還是放在 .jar 包中

四、插件化框架Task Hook

舉例:滴滴插件化框架學(xué)習(xí)筆記之virtualapk-gradle-plugin

https://juejin.im/post/6847902216590721038

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容