【Android 修炼手册】Gradle 篇 -- Gradle 源码分析
預(yù)備知識(shí)
看完本文可以達(dá)到什么程度
閱讀前準(zhǔn)備工作
讀代碼的姿勢(shì)
目錄
本文主要從下面幾個(gè)部分進(jìn)行分析
一、Gradle 的啟動(dòng)
1.1 整體實(shí)現(xiàn)圖
1.2 具體分析
我們執(zhí)行一個(gè)構(gòu)建任務(wù)的時(shí)候,都是執(zhí)行 ./gradlew assembleDebug 這樣的命令,其中的 gradlew 腳本就是整個(gè) gradle 構(gòu)建的入口,我們先從這里看起。
前面的代碼基本上就是判斷環(huán)境,設(shè)置變量的,直接看最后一行:
最后執(zhí)行的命令基本上如下:
exec $JAVA_HOME/bin/java -classpath $APP_HOME/gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain 復(fù)制代碼基本上可以看到,就是執(zhí)行了 gradle/wrapper/gradle-wrapper.jar 里的 org.gradle.wrapper.GradleWrapperMain,這樣我們就知道了,gradle 的入口類是 org.gradle.wrapper.GradleWrapperMain,也就知道代碼該從何開(kāi)始看了。
先看 GradleWrapperMain 的 main 函數(shù):
重要的類有兩個(gè) org.gradle.wrapper.WrapperExecutor 和 org.gradle.wrapper.BootstrapMainStarter。我們繼續(xù)跟進(jìn) WrapperExecutor.execute 里看一下:
// WrapperExecutor.execute public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception {File gradleHome = install.createDist(config);bootstrapMainStarter.start(args, gradleHome); } 復(fù)制代碼這里就做了兩件事:
如果創(chuàng)建過(guò)多個(gè)項(xiàng)目的話,我們?cè)?HOME/.gradle/wrapper/dists/ 里可以看到不同版本的 gradle wrapper,這也說(shuō)明了我們之前最開(kāi)始說(shuō)的,為什么要使用 gradle wrapper 而不是直接在電腦里安裝 gradle,就是因?yàn)?gradle wrapper 會(huì)根據(jù)不同的項(xiàng)目下載不同版本的內(nèi)容,項(xiàng)目彼此之間互不影響。
基本上構(gòu)建過(guò)程就是分五步走,下面分別看這五個(gè)流程。
二、loadSettings
2.1 整體實(shí)現(xiàn)圖
2.2 具體分析
loadSettings 主要是加載 settings.gradle 文件,然后創(chuàng)建對(duì)應(yīng)的 project。
// DefaultGradleLauncher.loadSettings private void loadSettings() {if (stage == null) {buildListener.buildStarted(gradle);buildOperationExecutor.run(new LoadBuild());stage = Stage.Load;} } 復(fù)制代碼整體構(gòu)建流程:
2.2.1 調(diào)用 BuildListener.buildStarted() 回調(diào)接口
通知構(gòu)建開(kāi)始。這個(gè)就是我們之前在 Gradle 基本使用 里說(shuō)的生命周期回調(diào)。
2.2.2 執(zhí)行 init 腳本
調(diào)用鏈路
LoadBuild.run -> InitScriptHandler.executeScripts 復(fù)制代碼之前在 Gradle 基本使用 里說(shuō)過(guò) init.gradle 的作用,會(huì)在每個(gè)項(xiàng)目 build 之前被調(diào)用,做一些初始化的操作,就是在這里被調(diào)用的。
2.2.3 查找 settings.gradle 位置
調(diào)用鏈路
LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> DefaultSettingsLoader.findSettings -> DefaultSettingsFinder.find -> BuildLayoutFactory.getLayoutFor 復(fù)制代碼實(shí)現(xiàn)分析
在 getLayoutFor 里,查找 settings.gradle 文件邏輯如下:
2.2.4 編譯 buildSrc 文件夾下的內(nèi)容,buildSrc 可以看作插件類似的功能
調(diào)用鏈路
LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> BuildSourceBuilder.buildAndCreateClassLoader 復(fù)制代碼在上一步找到 settings.gradle 文件以后,會(huì)以 settings.gradle 所在的同級(jí)目錄下,查找 buildSrc 目錄,并進(jìn)行編譯,這樣可以保證在構(gòu)建 settings.gradle 的時(shí)候可以引用到 buildSrc 目錄里的內(nèi)容。
2.2.5 解析 gradle.properites
調(diào)用鏈路
LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> NotifyingSettingsProcessor.process -> PropertiesLoadingSettingsProcessor.process -> DefaultGradlePropertiesLoader.loadProperties 復(fù)制代碼實(shí)現(xiàn)分析
這一步會(huì)讀取 gradle.properties 文件里的配置,系統(tǒng)配置,環(huán)境變量,以及命令行傳入的配置并存儲(chǔ)。
2.2.6 解析 settings.gradle
調(diào)用鏈路
LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> NotifyingSettingsProcessor.process -> PropertiesLoadingSettingsProcessor.process -> ScriptEvaluatingSettingsProcessor.process -> ScriptEvaluatingSettingsProcessor.applySettingsScript -> BuildOperationScriptPlugin.apply 復(fù)制代碼實(shí)現(xiàn)分析
在 ScriptEvaluatingSettingsProcessor 里,先創(chuàng)建了 SettingsInternal 實(shí)例,以及 ScriptSource 實(shí)例,代表 settings.gradle 文件在內(nèi)存中的映射,之后就調(diào)用 BuildOperationScriptPlugin.apply 去執(zhí)行 settings.gradle 文件了。
關(guān)于 BuildOperationScriptPlugin.apply,我們后面細(xì)說(shuō),因?yàn)樵诮馕?build.gradle 文件的時(shí)候也會(huì)用到這個(gè)方法。
下面是對(duì)應(yīng)的代碼:
2.2.7 創(chuàng)建 project 以及 subproject
調(diào)用鏈路
LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> NotifyingSettingsProcessor.process -> ProjectPropertySettingBuildLoader.load -> InstantiatingBuildLoader.load 復(fù)制代碼實(shí)現(xiàn)分析
在解析了 settings.gradle 文件以后,就可以知道項(xiàng)目里有哪些 project,就可以創(chuàng)建 project 實(shí)例了。
這里根據(jù) settings.gradle 的配置,創(chuàng)建項(xiàng)目實(shí)例。創(chuàng)建子項(xiàng)目的時(shí)候,如果父項(xiàng)目不為空,就將自己設(shè)置成父項(xiàng)目的子項(xiàng)目,這樣就可以通過(guò) project.getChildProjects 獲取項(xiàng)目的子項(xiàng)目了。
我們?cè)趯?xiě) gradle 腳本的時(shí)候,經(jīng)常會(huì)用到的 project 屬性,就是在這個(gè)時(shí)候創(chuàng)建出來(lái)了。
到此為止,就解析了 settings.gradle 文件然后創(chuàng)建了項(xiàng)目實(shí)例。
三、configureBuild
3.1 整體實(shí)現(xiàn)圖
3.2 具體分析
我們之前有說(shuō)到,gradle 構(gòu)建過(guò)程分為配置階段和運(yùn)行階段,配置階段主要是執(zhí)行腳本的內(nèi)容,運(yùn)行階段是執(zhí)行 task 的內(nèi)容,這里就是配置階段的流程。要注意,之前說(shuō)的配置和運(yùn)行階段,是從整體來(lái)看的兩個(gè)階段,從源碼來(lái)理解,就是這篇文章介紹的幾個(gè)階段,要更細(xì)化一點(diǎn)。
配置階段執(zhí)行的內(nèi)容比較簡(jiǎn)單,就是把 gradle 腳本編譯成 class 文件,然后運(yùn)行(gradle 是采用 groovy 語(yǔ)言編寫(xiě)的,groovy 是一門(mén) jvm 語(yǔ)言,所以必須要編譯成 class 才能運(yùn)行)。
在配置項(xiàng)目的時(shí)候,如果指定了 configure-on-demand 參數(shù),只會(huì)配置主項(xiàng)目以及執(zhí)行 task 需要的項(xiàng)目,默認(rèn)沒(méi)有指定,會(huì)配置所有的項(xiàng)目,這里只看默認(rèn)情況。
3.2.1 配置主項(xiàng)目及其子項(xiàng)目的主要鏈路
調(diào)用鏈路
ConfigureBuild.run -> DefaultBuildConfigurer.configure -> TaskPathProjectEvaluator.configureHierarchy -> TaskPathProjectEvaluator.configure -> DefaultProject.evaluate -> LifecycleProjectEvaluator.evaluate -> LifecycleProjectEvaluator.doConfigure -> ConfigureActionsProjectEvaluator.evaluate 復(fù)制代碼實(shí)現(xiàn)分析
// TaskPathProjectEvaluator public void configureHierarchy(ProjectInternal project) {configure(project);for (Project sub : project.getSubprojects()) {configure((ProjectInternal) sub);} } 復(fù)制代碼最終執(zhí)行到了 LifecycleProjectEvaluator.doConfigure
3.2.2 回調(diào) BuildListener.beforeEvaluate 接口
在這里回調(diào) beforeEvaluate 接口,通知配置將要開(kāi)始。我們也就知道了這個(gè)回調(diào)執(zhí)行的階段。
3.2.3 設(shè)置默認(rèn)的 task 和 插件
調(diào)用鏈路
ConfigureBuild.run -> DefaultBuildConfigurer.configure -> TaskPathProjectEvaluator.configureHierarchy -> TaskPathProjectEvaluator.configure -> DefaultProject.evaluate -> LifecycleProjectEvaluator.evaluate -> LifecycleProjectEvaluator.doConfigure -> ConfigureActionsProjectEvaluator.evaluate -> PluginsProjectConfigureActions.execute 復(fù)制代碼實(shí)現(xiàn)分析
在 PluginsProjectConfigureActions 里,會(huì)給 project 添加兩個(gè) task:init 和 wrapper,然后添加幫助插件:org.gradle.help-tasks。
3.2.4 編譯腳本并執(zhí)行
調(diào)用鏈路
ConfigureBuild.run -> DefaultBuildConfigurer.configure -> TaskPathProjectEvaluator.configureHierarchy -> TaskPathProjectEvaluator.configure -> DefaultProject.evaluate -> LifecycleProjectEvaluator.evaluate -> LifecycleProjectEvaluator.doConfigure -> ConfigureActionsProjectEvaluator.evaluate -> BuildScriptProcessor.execute -> BuildOperationScriptPlugin.apply 復(fù)制代碼實(shí)現(xiàn)分析
這里調(diào)用的還是 BuildOperationScriptPlugin.apply 去編譯和執(zhí)行 build.gradle 腳本,和前面解析 settings.gradle 是一樣的,這里我們先知道這個(gè)就是編譯 build.gradle 為 class。
文件并且執(zhí)行,然后先往后看流程,后面再詳細(xì)說(shuō)腳本是如何編譯和執(zhí)行的。
3.2.5 回調(diào) BuildListener.afterEvaluate
3.2.6 回調(diào) BuildListener.projectsEvaluated
四、constructTaskGraph
4.1 整體實(shí)現(xiàn)圖
4.2 具體分析
這一步是構(gòu)建 task 依賴圖
// DefaultGradleLauncher private void constructTaskGraph() {if (stage == Stage.Configure) {buildOperationExecutor.run(new CalculateTaskGraph());stage = Stage.TaskGraph;} } 復(fù)制代碼4.2.1 處理需要排除的 task
調(diào)用鏈路
CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> ExcludedTaskFilteringBuildConfigurationAction.configure 復(fù)制代碼實(shí)現(xiàn)分析
// ExcludedTaskFilteringBuildConfigurationAction public void configure(BuildExecutionContext context) {GradleInternal gradle = context.getGradle();Set<String> excludedTaskNames = gradle.getStartParameter().getExcludedTaskNames();if (!excludedTaskNames.isEmpty()) {final Set<Spec<Task>> filters = new HashSet<Spec<Task>>();for (String taskName : excludedTaskNames) {filters.add(taskSelector.getFilter(taskName));}gradle.getTaskGraph().useFilter(Specs.intersect(filters));}context.proceed(); } 復(fù)制代碼這一步是用來(lái)處理需要排除的 task,也就是在命令行通過(guò) -x or --exclude-task 指定的 task,這里主要是給 TaskGraph 設(shè)置了 filter,以便在后面計(jì)算依賴的時(shí)候排除相應(yīng)的 task。
4.2.2 添加默認(rèn)的 task
調(diào)用鏈路
CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> DefaultTasksBuildExecutionAction.configure 復(fù)制代碼實(shí)現(xiàn)分析
這里會(huì)檢查命令行里是否有傳入 Task 名稱進(jìn)來(lái),如果指定了要執(zhí)行的 task,那么什么都不做。
如果沒(méi)有指定,就看 project 是否有默認(rèn)的 task,默認(rèn)的 task 可以通過(guò) defaultTasks 在 build.gradle 里進(jìn)行指定。
如果也默認(rèn) task 也沒(méi)有,那么就把要指定的 task 設(shè)置成 help task,也就是輸出 gradle 的幫助內(nèi)容。
4.2.3 計(jì)算 task 依賴圖
調(diào)用鏈路
CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> TaskNameResolvingBuildConfigurationAction.configure 復(fù)制代碼實(shí)現(xiàn)分析
4.2.4 生成 task graph
調(diào)用鏈路
CalculateTaskGraph.run -> TaskGraphExecuter.populate -> DefaultTaskExecutionPlan.determineExecutionPlan 復(fù)制代碼實(shí)現(xiàn)分析
根據(jù)上一步計(jì)算的 task 及其依賴,生成 task 圖
五、runTasks
5.1 整體實(shí)現(xiàn)圖
5.2 具體分析
task 圖生成以后,就開(kāi)始執(zhí)行 task
5.2.1 處理 dry run
調(diào)用鏈路
DefaultBuildExecuter.execute -> DryRunBuildExecutionAction.execute 復(fù)制代碼實(shí)現(xiàn)分析
如果在命令行里指定了 --dry-run,在這里就會(huì)攔截 task 的執(zhí)行,直接輸出 task 的名稱以及執(zhí)行的先后關(guān)系。
5.2.2 創(chuàng)建線程,執(zhí)行 task
調(diào)用鏈路
DefaultBuildExecuter.execute -> SelectedTaskExecutionAction.execute -> DefaultTaskPlanExecutor.process 復(fù)制代碼實(shí)現(xiàn)分析
創(chuàng)建 TaskExecutorWorker 去執(zhí)行 task,默認(rèn)是 8 個(gè)線程。
5.2.3 task 執(zhí)行前處理
調(diào)用鏈路
DefaultBuildExecuter.execute -> SelectedTaskExecutionAction.execute -> DefaultTaskPlanExecutor.process -> TaskExecutorWorker.run -> DefaultTaskExecutionPlan.executeWithTask -> DefaultTaskExecutionPlan.selectNextTask -> DefaultTaskExecutionPlan.processTask -> EventFiringTaskWorker.execute -> DefaultBuildOperationExecutor.run 復(fù)制代碼實(shí)現(xiàn)分析
到這里就正式開(kāi)始 task 的執(zhí)行過(guò)程了。有幾個(gè)步驟:
5.2.4 task 執(zhí)行
調(diào)用鏈路
DefaultBuildExecuter.execute -> SelectedTaskExecutionAction.execute -> DefaultTaskPlanExecutor.process -> TaskExecutorWorker.run -> DefaultTaskExecutionPlan.executeWithTask -> DefaultTaskExecutionPlan.selectNextTask -> DefaultTaskExecutionPlan.processTask -> EventFiringTaskWorker.execute -> DefaultBuildOperationExecutor.run -> ExecuteActionsTaskExecuter.execute 復(fù)制代碼實(shí)現(xiàn)分析
經(jīng)過(guò)前面一系列處理,這里開(kāi)始真正執(zhí)行 task 了。
這里可以看到,Task 的本質(zhì),其實(shí)就是執(zhí)行其中的 Actions。舉個(gè)例子來(lái)說(shuō),我們一般自定義 Task 的時(shí)候,經(jīng)常用下面的寫(xiě)法:
task {doLast {// task 具體任務(wù)} } 復(fù)制代碼這里的 doLast 就相當(dāng)于給 Task 添加了一個(gè) Action。
看一下 AbstractTask 的 doLast 方法
可以看到,我們傳入的閉包,最終是包裝成 TaskActionWrapper 添加到 task 的 actions 中的。
六、finishBuild
6.1 整體實(shí)現(xiàn)圖
6.2 具體分析
private void finishBuild(BuildResult result) {if (stage == Stage.Finished) {return;}buildListener.buildFinished(result);if (!isNestedBuild()) {gradle.getServices().get(IncludedBuildControllers.class).stopTaskExecution();}stage = Stage.Finished; } 復(fù)制代碼這里邏輯不多,回調(diào)了 BuildListener.buildFinished 接口
通過(guò)上面幾個(gè)步驟,我們基本上看到了 gradle 的執(zhí)行流程,簡(jiǎn)單來(lái)說(shuō),步驟如下:
七、gradle 腳本如何編譯和執(zhí)行
在前面介紹 loadSettings 和 configureBuild 階段的時(shí)候,我們提到了 BuildOperationScriptPlugin.apply 這個(gè)方法,只是簡(jiǎn)單帶過(guò),是用來(lái)編譯 gradle 腳本并執(zhí)行的,這里來(lái)具體分析一下。
7.1 編譯腳本
調(diào)用鏈路
BuildOperationScriptPlugin.apply -> DefaultScriptPluginFactory.ScriptPluginImpl.apply -> DefaultScriptCompilerFactory.ScriptCompilerImpl.compile -> BuildScopeInMemoryCachingScriptClassCompiler.compile -> CrossBuildInMemoryCachingScriptClassCache.getOrCompile -> FileCacheBackedScriptClassCompiler.compile 復(fù)制代碼實(shí)現(xiàn)分析
這里編譯過(guò)程分為兩部分,首先編譯腳本的 buildscript {} 部分,忽略其他部分,然后再編譯腳本的其他部分并執(zhí)行。所以 buildscript {} 里的內(nèi)容會(huì)先于其他內(nèi)容執(zhí)行。
會(huì)先檢查緩存,如果有緩存的話,直接使用,沒(méi)有緩存再進(jìn)行編譯
最終會(huì)調(diào)用到 CompileToCrossBuildCacheAction.execute -> DefaultScriptCompilationHandler.compileToDir -> DefaultScriptCompilationHandler.compileScript 去執(zhí)行真正的編譯操作
腳本緩存路徑: /Users/zy/.gradle/caches/4.1/scripts-remapped/build_a3v29m9cbrge95ug6eejz9wuw/31f5shvfkfunwn5ullupyy7xt/cp_proj4dada6424967ba8dfea75e81c8880f7f/classes
目錄下的 class 如下:
具體編譯方法是通過(guò) RemappingScriptSource.getResource().getText() 獲取到腳本內(nèi)容,然后通過(guò) GroovyClassLoader.parseClass 編譯的。
我們以 app/build.gradle 為例,看一下最終生成的腳本是什么樣子的。
build.gradle 腳本內(nèi)容
編譯后 class 內(nèi)容
package defpackage;import groovy.lang.MetaClass; import java.lang.ref.SoftReference; import org.codehaus.groovy.reflection.ClassInfo; import org.codehaus.groovy.runtime.GStringImpl; import org.codehaus.groovy.runtime.ScriptBytecodeAdapter; import org.codehaus.groovy.runtime.callsite.CallSite; import org.codehaus.groovy.runtime.callsite.CallSiteArray; import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling; import org.gradle.api.internal.project.ProjectScript; import org.gradle.internal.scripts.ScriptOrigin;/* compiled from: /Users/zy/workspace/note/blog/android-training/gradle/EasyGradle/app/build.gradle */ public class build_ak168fqfikdepd6py4yef8tgs extends ProjectScript implements ScriptOrigin {private static /* synthetic */ SoftReference $callSiteArray = null;private static /* synthetic */ ClassInfo $staticClassInfo = null;public static transient /* synthetic */ boolean __$stMC = false;private static final /* synthetic */ String __originalClassName = "_BuildScript_";private static final /* synthetic */ String __signature = "988274f32891a2a3d3b8d16074617c05";private static /* synthetic */ CallSiteArray $createCallSiteArray() {String[] strArr = new String[22];build_ak168fqfikdepd6py4yef8tgs.$createCallSiteArray_1(strArr);return new CallSiteArray(build_ak168fqfikdepd6py4yef8tgs.class, strArr);}private static /* synthetic */ void $createCallSiteArray_1(String[] strArr) {strArr[0] = "apply";strArr[1] = "apply";strArr[2] = "android";strArr[3] = "dependencies";strArr[4] = "addBuildListener";strArr[5] = "gradle";strArr[6] = "addProjectEvaluationListener";strArr[7] = "gradle";strArr[8] = "whenReady";strArr[9] = "taskGraph";strArr[10] = "gradle";strArr[11] = "beforeTask";strArr[12] = "taskGraph";strArr[13] = "gradle";strArr[14] = "afterTask";strArr[15] = "taskGraph";strArr[16] = "gradle";strArr[17] = "task";strArr[18] = "task";strArr[19] = "finalizedBy";strArr[20] = "task1";strArr[21] = "task2";}/* JADX WARNING: inconsistent code. *//* Code decompiled incorrectly, please refer to instructions dump. */private static /* synthetic */ org.codehaus.groovy.runtime.callsite.CallSite[] $getCallSiteArray() {/*r0 = $callSiteArray;if (r0 == 0) goto L_0x000e;L_0x0004:r0 = $callSiteArray;r0 = r0.get();r0 = (org.codehaus.groovy.runtime.callsite.CallSiteArray) r0;if (r0 != 0) goto L_0x0019;L_0x000e:r0 = defpackage.build_ak168fqfikdepd6py4yef8tgs.$createCallSiteArray();r1 = new java.lang.ref.SoftReference;r1.<init>(r0);$callSiteArray = r1;L_0x0019:r0 = r0.array;return r0;*/throw new UnsupportedOperationException("Method not decompiled: build_ak168fqfikdepd6py4yef8tgs.$getCallSiteArray():org.codehaus.groovy.runtime.callsite.CallSite[]");}public build_ak168fqfikdepd6py4yef8tgs() {build_ak168fqfikdepd6py4yef8tgs.$getCallSiteArray();}protected /* synthetic */ MetaClass $getStaticMetaClass() {if (getClass() != build_ak168fqfikdepd6py4yef8tgs.class) {return ScriptBytecodeAdapter.initMetaClass(this);}ClassInfo classInfo = $staticClassInfo;if (classInfo == null) {classInfo = ClassInfo.getClassInfo(getClass());$staticClassInfo = classInfo;}return classInfo.getMetaClass();}public String getContentHash() {return __signature;}public String getOriginalClassName() {return __originalClassName;}public Object run() {CallSite[] $getCallSiteArray = build_ak168fqfikdepd6py4yef8tgs.$getCallSiteArray();$getCallSiteArray[0].callCurrent(this, ScriptBytecodeAdapter.createMap(new Object[]{"plugin", "com.android.application"}));$getCallSiteArray[1].callCurrent(this, ScriptBytecodeAdapter.createMap(new Object[]{"plugin", "myplugin"}));$getCallSiteArray[2].callCurrent(this, new _run_closure1(this, this));$getCallSiteArray[3].callCurrent(this, new _run_closure2(this, this));$getCallSiteArray[4].call($getCallSiteArray[5].callGroovyObjectGetProperty(this), new 1(this));$getCallSiteArray[6].call($getCallSiteArray[7].callGroovyObjectGetProperty(this), new 2(this));$getCallSiteArray[8].call($getCallSiteArray[9].callGetProperty($getCallSiteArray[10].callGroovyObjectGetProperty(this)), new _run_closure3(this, this));$getCallSiteArray[11].call($getCallSiteArray[12].callGetProperty($getCallSiteArray[13].callGroovyObjectGetProperty(this)), new _run_closure4(this, this));$getCallSiteArray[14].call($getCallSiteArray[15].callGetProperty($getCallSiteArray[16].callGroovyObjectGetProperty(this)), new _run_closure5(this, this));$getCallSiteArray[17].callCurrent(this, "task1", new _run_closure6(this, this));$getCallSiteArray[18].callCurrent(this, "task2", new _run_closure7(this, this));return $getCallSiteArray[19].call($getCallSiteArray[20].callGroovyObjectGetProperty(this), $getCallSiteArray[21].callGroovyObjectGetProperty(this));}public /* synthetic */ Object this$dist$get$7(String name) {build_ak168fqfikdepd6py4yef8tgs.$getCallSiteArray();return ScriptBytecodeAdapter.getGroovyObjectProperty(build_ak168fqfikdepd6py4yef8tgs.class, this, ShortTypeHandling.castToString(new GStringImpl(new Object[]{name}, new String[]{"", ""})));}public /* synthetic */ Object this$dist$invoke$7(String name, Object args) {build_ak168fqfikdepd6py4yef8tgs.$getCallSiteArray();return ScriptBytecodeAdapter.invokeMethodOnCurrentN(build_ak168fqfikdepd6py4yef8tgs.class, this, ShortTypeHandling.castToString(new GStringImpl(new Object[]{name}, new String[]{"", ""})), ScriptBytecodeAdapter.despreadList(new Object[0], new Object[]{args}, new int[]{0}));}public /* synthetic */ void this$dist$set$7(String name, Object value) {build_ak168fqfikdepd6py4yef8tgs.$getCallSiteArray();ScriptBytecodeAdapter.setGroovyObjectProperty(value, build_ak168fqfikdepd6py4yef8tgs.class, this, ShortTypeHandling.castToString(new GStringImpl(new Object[]{name}, new String[]{"", ""})));} } 復(fù)制代碼可以看到,腳本類繼承自 ProjectScript,實(shí)現(xiàn)了 run 方法。
run 方法里做了些什么呢,先看第一行,
獲取到 callsiteArray,這個(gè)就是 createCallSiteArray_1() 方法中賦值的,可以看到,此處的 callsiteArray,都是腳本中的 dsl,其實(shí)也就是調(diào)用的方法名。 獲取到 callsiteArray 以后,執(zhí)行 $getCallSiteArray[0].callCurrent() 類似的方法,這個(gè)就是在調(diào)用方法。調(diào)用的方法對(duì)應(yīng)的腳本代碼在下面加了注釋。
public Object run() {CallSite[] $getCallSiteArray = build_ak168fqfikdepd6py4yef8tgs.$getCallSiteArray();// apply plugin "com.android.application" 依賴插件$getCallSiteArray[0].callCurrent(this, ScriptBytecodeAdapter.createMap(new Object[]{"plugin", "com.android.application"}));// apply plugin myplugin$getCallSiteArray[1].callCurrent(this, ScriptBytecodeAdapter.createMap(new Object[]{"plugin", "myplugin"}));// android {}$getCallSiteArray[2].callCurrent(this, new _run_closure1(this, this));// dependencies {} $getCallSiteArray[3].callCurrent(this, new _run_closure2(this, this));// task {}$getCallSiteArray[17].callCurrent(this, "task1", new _run_closure6(this, this));// ...return $getCallSiteArray[19].call($getCallSiteArray[20].callGroovyObjectGetProperty(this), $getCallSiteArray[21].callGroovyObjectGetProperty(this)); } 復(fù)制代碼上面看到,task1 對(duì)應(yīng)的是 _run_closure6 這個(gè)類,我們看看這個(gè)類的內(nèi)容。
/* compiled from: /Users/zy/workspace/note/blog/android-training/gradle/EasyGradle/app/build.gradle */ public class build_ak168fqfikdepd6py4yef8tgs$_run_closure6 extends Closure implements GeneratedClosure, ScriptOrigin {private static final /* synthetic */ String __originalClassName = "_BuildScript_$_run_closure6";private static /* synthetic */ CallSiteArray $createCallSiteArray() {String[] strArr = new String[1];strArr[0] = "doLast";return new CallSiteArray(build_ak168fqfikdepd6py4yef8tgs$_run_closure6.class, strArr);}public build_ak168fqfikdepd6py4yef8tgs$_run_closure6(Object _outerInstance, Object _thisObject) {build_ak168fqfikdepd6py4yef8tgs$_run_closure6.$getCallSiteArray();super(_outerInstance, _thisObject);}public Object doCall() {build_ak168fqfikdepd6py4yef8tgs$_run_closure6.$getCallSiteArray();return doCall(null);}public Object doCall(Object it) {return build_ak168fqfikdepd6py4yef8tgs$_run_closure6.$getCallSiteArray()[0].callCurrent(this, new _closure17(this, getThisObject()));} } 復(fù)制代碼省略了一些內(nèi)容,可以看到,這個(gè)閉包的類繼承了 Closure,然后實(shí)現(xiàn)了 doCall 方法,在 doCall 方法里,調(diào)用了 doLast 方法,傳入了 _closure17 實(shí)例。這個(gè)就是腳本中的 task { doLast {} } 對(duì)應(yīng)的實(shí)現(xiàn)。
我們?cè)倏纯?_closure17 的實(shí)現(xiàn)。
同樣也是繼承了 Closure,在 doCall 方法里調(diào)用了 println,這正是我們?cè)?task 的里執(zhí)行的任務(wù),也就是前面提到的 task 的 actions。
這里我們?cè)倮眄樢幌?#xff0c;每一個(gè) build.gradle 腳本,對(duì)應(yīng)一個(gè)繼承了 ProjectScript 的類,每一個(gè)閉包,對(duì)應(yīng)了一個(gè)繼承自 Closure 的類
7.2 調(diào)用腳本 run 方法
接著就是執(zhí)行腳本類的 run 方法,也就是我們?cè)谏厦娣治龅?run 方法。
其中強(qiáng)調(diào)的一點(diǎn)是,run 方法里對(duì) task 的創(chuàng)建,僅僅是執(zhí)行了 task.doCall,這也就是為什么配置階段不會(huì)執(zhí)行 task 任務(wù),但會(huì)執(zhí)行 task 閉包里的內(nèi)容。
八、插件調(diào)用流程
之前在 Gradle的基本使用 里講到過(guò)自定義插件,使用的時(shí)候是通過(guò) apply plugin 'xxx' 來(lái)使用的,具體的調(diào)用鏈路如下:
apply: "xxx" -> Script.run -> ProjectScript.apply -> DefaultObjectConfigurationAction.run -> DefaultObjectConfigurationAction.applyType(pluginId) -> DefaultPluginManager.apply -> DefaultPluginManager.AddPluginBuildOperation.run -> AddPluginBuildOperation.addPlugin -> RuleBasedPluginTarget.applyImpreative -> ImperativeOnlyPluginTarget.applyImperative -> Plugin.apply 復(fù)制代碼最后的 Plugin.apply 就調(diào)用到插件里實(shí)現(xiàn)的 apply() 函數(shù)了
九、總結(jié)
整體結(jié)構(gòu)圖
【Android 修煉手冊(cè)】系列內(nèi)容 每周更新 歡迎關(guān)注下面賬號(hào),獲取更新:
微信搜索公眾號(hào): ZYLAB
Github
知乎
掘金
總結(jié)
以上是生活随笔為你收集整理的【Android 修炼手册】Gradle 篇 -- Gradle 源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 8421BCD码 5421BCD码 余三
- 下一篇: 【Android 修炼手册】常用技术篇