【Android 修炼手册】Gradle 篇 -- Gradle 源码分析
預備知識
看完本文可以達到什么程度
閱讀前準備工作
讀代碼的姿勢
目錄
本文主要從下面幾個部分進行分析
一、Gradle 的啟動
1.1 整體實現圖
1.2 具體分析
我們執(zhí)行一個構建任務的時候,都是執(zhí)行 ./gradlew assembleDebug 這樣的命令,其中的 gradlew 腳本就是整個 gradle 構建的入口,我們先從這里看起。
前面的代碼基本上就是判斷環(huán)境,設置變量的,直接看最后一行:
最后執(zhí)行的命令基本上如下:
exec $JAVA_HOME/bin/java -classpath $APP_HOME/gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain 復制代碼基本上可以看到,就是執(zhí)行了 gradle/wrapper/gradle-wrapper.jar 里的 org.gradle.wrapper.GradleWrapperMain,這樣我們就知道了,gradle 的入口類是 org.gradle.wrapper.GradleWrapperMain,也就知道代碼該從何開始看了。
先看 GradleWrapperMain 的 main 函數:
重要的類有兩個 org.gradle.wrapper.WrapperExecutor 和 org.gradle.wrapper.BootstrapMainStarter。我們繼續(xù)跟進 WrapperExecutor.execute 里看一下:
// WrapperExecutor.execute public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception {File gradleHome = install.createDist(config);bootstrapMainStarter.start(args, gradleHome); } 復制代碼這里就做了兩件事:
如果創(chuàng)建過多個項目的話,我們在 HOME/.gradle/wrapper/dists/ 里可以看到不同版本的 gradle wrapper,這也說明了我們之前最開始說的,為什么要使用 gradle wrapper 而不是直接在電腦里安裝 gradle,就是因為 gradle wrapper 會根據不同的項目下載不同版本的內容,項目彼此之間互不影響。
基本上構建過程就是分五步走,下面分別看這五個流程。
二、loadSettings
2.1 整體實現圖
2.2 具體分析
loadSettings 主要是加載 settings.gradle 文件,然后創(chuàng)建對應的 project。
// DefaultGradleLauncher.loadSettings private void loadSettings() {if (stage == null) {buildListener.buildStarted(gradle);buildOperationExecutor.run(new LoadBuild());stage = Stage.Load;} } 復制代碼整體構建流程:
2.2.1 調用 BuildListener.buildStarted() 回調接口
通知構建開始。這個就是我們之前在 Gradle 基本使用 里說的生命周期回調。
2.2.2 執(zhí)行 init 腳本
調用鏈路
LoadBuild.run -> InitScriptHandler.executeScripts 復制代碼之前在 Gradle 基本使用 里說過 init.gradle 的作用,會在每個項目 build 之前被調用,做一些初始化的操作,就是在這里被調用的。
2.2.3 查找 settings.gradle 位置
調用鏈路
LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> DefaultSettingsLoader.findSettings -> DefaultSettingsFinder.find -> BuildLayoutFactory.getLayoutFor 復制代碼實現分析
在 getLayoutFor 里,查找 settings.gradle 文件邏輯如下:
2.2.4 編譯 buildSrc 文件夾下的內容,buildSrc 可以看作插件類似的功能
調用鏈路
LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> BuildSourceBuilder.buildAndCreateClassLoader 復制代碼在上一步找到 settings.gradle 文件以后,會以 settings.gradle 所在的同級目錄下,查找 buildSrc 目錄,并進行編譯,這樣可以保證在構建 settings.gradle 的時候可以引用到 buildSrc 目錄里的內容。
2.2.5 解析 gradle.properites
調用鏈路
LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> NotifyingSettingsProcessor.process -> PropertiesLoadingSettingsProcessor.process -> DefaultGradlePropertiesLoader.loadProperties 復制代碼實現分析
這一步會讀取 gradle.properties 文件里的配置,系統(tǒng)配置,環(huán)境變量,以及命令行傳入的配置并存儲。
2.2.6 解析 settings.gradle
調用鏈路
LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> NotifyingSettingsProcessor.process -> PropertiesLoadingSettingsProcessor.process -> ScriptEvaluatingSettingsProcessor.process -> ScriptEvaluatingSettingsProcessor.applySettingsScript -> BuildOperationScriptPlugin.apply 復制代碼實現分析
在 ScriptEvaluatingSettingsProcessor 里,先創(chuàng)建了 SettingsInternal 實例,以及 ScriptSource 實例,代表 settings.gradle 文件在內存中的映射,之后就調用 BuildOperationScriptPlugin.apply 去執(zhí)行 settings.gradle 文件了。
關于 BuildOperationScriptPlugin.apply,我們后面細說,因為在解析 build.gradle 文件的時候也會用到這個方法。
下面是對應的代碼:
2.2.7 創(chuàng)建 project 以及 subproject
調用鏈路
LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> NotifyingSettingsProcessor.process -> ProjectPropertySettingBuildLoader.load -> InstantiatingBuildLoader.load 復制代碼實現分析
在解析了 settings.gradle 文件以后,就可以知道項目里有哪些 project,就可以創(chuàng)建 project 實例了。
這里根據 settings.gradle 的配置,創(chuàng)建項目實例。創(chuàng)建子項目的時候,如果父項目不為空,就將自己設置成父項目的子項目,這樣就可以通過 project.getChildProjects 獲取項目的子項目了。
我們在寫 gradle 腳本的時候,經常會用到的 project 屬性,就是在這個時候創(chuàng)建出來了。
到此為止,就解析了 settings.gradle 文件然后創(chuàng)建了項目實例。
三、configureBuild
3.1 整體實現圖
3.2 具體分析
我們之前有說到,gradle 構建過程分為配置階段和運行階段,配置階段主要是執(zhí)行腳本的內容,運行階段是執(zhí)行 task 的內容,這里就是配置階段的流程。要注意,之前說的配置和運行階段,是從整體來看的兩個階段,從源碼來理解,就是這篇文章介紹的幾個階段,要更細化一點。
配置階段執(zhí)行的內容比較簡單,就是把 gradle 腳本編譯成 class 文件,然后運行(gradle 是采用 groovy 語言編寫的,groovy 是一門 jvm 語言,所以必須要編譯成 class 才能運行)。
在配置項目的時候,如果指定了 configure-on-demand 參數,只會配置主項目以及執(zhí)行 task 需要的項目,默認沒有指定,會配置所有的項目,這里只看默認情況。
3.2.1 配置主項目及其子項目的主要鏈路
調用鏈路
ConfigureBuild.run -> DefaultBuildConfigurer.configure -> TaskPathProjectEvaluator.configureHierarchy -> TaskPathProjectEvaluator.configure -> DefaultProject.evaluate -> LifecycleProjectEvaluator.evaluate -> LifecycleProjectEvaluator.doConfigure -> ConfigureActionsProjectEvaluator.evaluate 復制代碼實現分析
// TaskPathProjectEvaluator public void configureHierarchy(ProjectInternal project) {configure(project);for (Project sub : project.getSubprojects()) {configure((ProjectInternal) sub);} } 復制代碼最終執(zhí)行到了 LifecycleProjectEvaluator.doConfigure
3.2.2 回調 BuildListener.beforeEvaluate 接口
在這里回調 beforeEvaluate 接口,通知配置將要開始。我們也就知道了這個回調執(zhí)行的階段。
3.2.3 設置默認的 task 和 插件
調用鏈路
ConfigureBuild.run -> DefaultBuildConfigurer.configure -> TaskPathProjectEvaluator.configureHierarchy -> TaskPathProjectEvaluator.configure -> DefaultProject.evaluate -> LifecycleProjectEvaluator.evaluate -> LifecycleProjectEvaluator.doConfigure -> ConfigureActionsProjectEvaluator.evaluate -> PluginsProjectConfigureActions.execute 復制代碼實現分析
在 PluginsProjectConfigureActions 里,會給 project 添加兩個 task:init 和 wrapper,然后添加幫助插件:org.gradle.help-tasks。
3.2.4 編譯腳本并執(zhí)行
調用鏈路
ConfigureBuild.run -> DefaultBuildConfigurer.configure -> TaskPathProjectEvaluator.configureHierarchy -> TaskPathProjectEvaluator.configure -> DefaultProject.evaluate -> LifecycleProjectEvaluator.evaluate -> LifecycleProjectEvaluator.doConfigure -> ConfigureActionsProjectEvaluator.evaluate -> BuildScriptProcessor.execute -> BuildOperationScriptPlugin.apply 復制代碼實現分析
這里調用的還是 BuildOperationScriptPlugin.apply 去編譯和執(zhí)行 build.gradle 腳本,和前面解析 settings.gradle 是一樣的,這里我們先知道這個就是編譯 build.gradle 為 class。
文件并且執(zhí)行,然后先往后看流程,后面再詳細說腳本是如何編譯和執(zhí)行的。
3.2.5 回調 BuildListener.afterEvaluate
3.2.6 回調 BuildListener.projectsEvaluated
四、constructTaskGraph
4.1 整體實現圖
4.2 具體分析
這一步是構建 task 依賴圖
// DefaultGradleLauncher private void constructTaskGraph() {if (stage == Stage.Configure) {buildOperationExecutor.run(new CalculateTaskGraph());stage = Stage.TaskGraph;} } 復制代碼4.2.1 處理需要排除的 task
調用鏈路
CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> ExcludedTaskFilteringBuildConfigurationAction.configure 復制代碼實現分析
// 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(); } 復制代碼這一步是用來處理需要排除的 task,也就是在命令行通過 -x or --exclude-task 指定的 task,這里主要是給 TaskGraph 設置了 filter,以便在后面計算依賴的時候排除相應的 task。
4.2.2 添加默認的 task
調用鏈路
CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> DefaultTasksBuildExecutionAction.configure 復制代碼實現分析
這里會檢查命令行里是否有傳入 Task 名稱進來,如果指定了要執(zhí)行的 task,那么什么都不做。
如果沒有指定,就看 project 是否有默認的 task,默認的 task 可以通過 defaultTasks 在 build.gradle 里進行指定。
如果也默認 task 也沒有,那么就把要指定的 task 設置成 help task,也就是輸出 gradle 的幫助內容。
4.2.3 計算 task 依賴圖
調用鏈路
CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> TaskNameResolvingBuildConfigurationAction.configure 復制代碼實現分析
4.2.4 生成 task graph
調用鏈路
CalculateTaskGraph.run -> TaskGraphExecuter.populate -> DefaultTaskExecutionPlan.determineExecutionPlan 復制代碼實現分析
根據上一步計算的 task 及其依賴,生成 task 圖
五、runTasks
5.1 整體實現圖
5.2 具體分析
task 圖生成以后,就開始執(zhí)行 task
5.2.1 處理 dry run
調用鏈路
DefaultBuildExecuter.execute -> DryRunBuildExecutionAction.execute 復制代碼實現分析
如果在命令行里指定了 --dry-run,在這里就會攔截 task 的執(zhí)行,直接輸出 task 的名稱以及執(zhí)行的先后關系。
5.2.2 創(chuàng)建線程,執(zhí)行 task
調用鏈路
DefaultBuildExecuter.execute -> SelectedTaskExecutionAction.execute -> DefaultTaskPlanExecutor.process 復制代碼實現分析
創(chuàng)建 TaskExecutorWorker 去執(zhí)行 task,默認是 8 個線程。
5.2.3 task 執(zhí)行前處理
調用鏈路
DefaultBuildExecuter.execute -> SelectedTaskExecutionAction.execute -> DefaultTaskPlanExecutor.process -> TaskExecutorWorker.run -> DefaultTaskExecutionPlan.executeWithTask -> DefaultTaskExecutionPlan.selectNextTask -> DefaultTaskExecutionPlan.processTask -> EventFiringTaskWorker.execute -> DefaultBuildOperationExecutor.run 復制代碼實現分析
到這里就正式開始 task 的執(zhí)行過程了。有幾個步驟:
5.2.4 task 執(zhí)行
調用鏈路
DefaultBuildExecuter.execute -> SelectedTaskExecutionAction.execute -> DefaultTaskPlanExecutor.process -> TaskExecutorWorker.run -> DefaultTaskExecutionPlan.executeWithTask -> DefaultTaskExecutionPlan.selectNextTask -> DefaultTaskExecutionPlan.processTask -> EventFiringTaskWorker.execute -> DefaultBuildOperationExecutor.run -> ExecuteActionsTaskExecuter.execute 復制代碼實現分析
經過前面一系列處理,這里開始真正執(zhí)行 task 了。
這里可以看到,Task 的本質,其實就是執(zhí)行其中的 Actions。舉個例子來說,我們一般自定義 Task 的時候,經常用下面的寫法:
task {doLast {// task 具體任務} } 復制代碼這里的 doLast 就相當于給 Task 添加了一個 Action。
看一下 AbstractTask 的 doLast 方法
可以看到,我們傳入的閉包,最終是包裝成 TaskActionWrapper 添加到 task 的 actions 中的。
六、finishBuild
6.1 整體實現圖
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; } 復制代碼這里邏輯不多,回調了 BuildListener.buildFinished 接口
通過上面幾個步驟,我們基本上看到了 gradle 的執(zhí)行流程,簡單來說,步驟如下:
七、gradle 腳本如何編譯和執(zhí)行
在前面介紹 loadSettings 和 configureBuild 階段的時候,我們提到了 BuildOperationScriptPlugin.apply 這個方法,只是簡單帶過,是用來編譯 gradle 腳本并執(zhí)行的,這里來具體分析一下。
7.1 編譯腳本
調用鏈路
BuildOperationScriptPlugin.apply -> DefaultScriptPluginFactory.ScriptPluginImpl.apply -> DefaultScriptCompilerFactory.ScriptCompilerImpl.compile -> BuildScopeInMemoryCachingScriptClassCompiler.compile -> CrossBuildInMemoryCachingScriptClassCache.getOrCompile -> FileCacheBackedScriptClassCompiler.compile 復制代碼實現分析
這里編譯過程分為兩部分,首先編譯腳本的 buildscript {} 部分,忽略其他部分,然后再編譯腳本的其他部分并執(zhí)行。所以 buildscript {} 里的內容會先于其他內容執(zhí)行。
會先檢查緩存,如果有緩存的話,直接使用,沒有緩存再進行編譯
最終會調用到 CompileToCrossBuildCacheAction.execute -> DefaultScriptCompilationHandler.compileToDir -> DefaultScriptCompilationHandler.compileScript 去執(zhí)行真正的編譯操作
腳本緩存路徑: /Users/zy/.gradle/caches/4.1/scripts-remapped/build_a3v29m9cbrge95ug6eejz9wuw/31f5shvfkfunwn5ullupyy7xt/cp_proj4dada6424967ba8dfea75e81c8880f7f/classes
目錄下的 class 如下:
具體編譯方法是通過 RemappingScriptSource.getResource().getText() 獲取到腳本內容,然后通過 GroovyClassLoader.parseClass 編譯的。
我們以 app/build.gradle 為例,看一下最終生成的腳本是什么樣子的。
build.gradle 腳本內容
編譯后 class 內容
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[]{"", ""})));} } 復制代碼可以看到,腳本類繼承自 ProjectScript,實現了 run 方法。
run 方法里做了些什么呢,先看第一行,
獲取到 callsiteArray,這個就是 createCallSiteArray_1() 方法中賦值的,可以看到,此處的 callsiteArray,都是腳本中的 dsl,其實也就是調用的方法名。 獲取到 callsiteArray 以后,執(zhí)行 $getCallSiteArray[0].callCurrent() 類似的方法,這個就是在調用方法。調用的方法對應的腳本代碼在下面加了注釋。
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)); } 復制代碼上面看到,task1 對應的是 _run_closure6 這個類,我們看看這個類的內容。
/* 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()));} } 復制代碼省略了一些內容,可以看到,這個閉包的類繼承了 Closure,然后實現了 doCall 方法,在 doCall 方法里,調用了 doLast 方法,傳入了 _closure17 實例。這個就是腳本中的 task { doLast {} } 對應的實現。
我們再看看 _closure17 的實現。
同樣也是繼承了 Closure,在 doCall 方法里調用了 println,這正是我們在 task 的里執(zhí)行的任務,也就是前面提到的 task 的 actions。
這里我們再理順一下,每一個 build.gradle 腳本,對應一個繼承了 ProjectScript 的類,每一個閉包,對應了一個繼承自 Closure 的類
7.2 調用腳本 run 方法
接著就是執(zhí)行腳本類的 run 方法,也就是我們在上面分析的 run 方法。
其中強調的一點是,run 方法里對 task 的創(chuàng)建,僅僅是執(zhí)行了 task.doCall,這也就是為什么配置階段不會執(zhí)行 task 任務,但會執(zhí)行 task 閉包里的內容。
八、插件調用流程
之前在 Gradle的基本使用 里講到過自定義插件,使用的時候是通過 apply plugin 'xxx' 來使用的,具體的調用鏈路如下:
apply: "xxx" -> Script.run -> ProjectScript.apply -> DefaultObjectConfigurationAction.run -> DefaultObjectConfigurationAction.applyType(pluginId) -> DefaultPluginManager.apply -> DefaultPluginManager.AddPluginBuildOperation.run -> AddPluginBuildOperation.addPlugin -> RuleBasedPluginTarget.applyImpreative -> ImperativeOnlyPluginTarget.applyImperative -> Plugin.apply 復制代碼最后的 Plugin.apply 就調用到插件里實現的 apply() 函數了
九、總結
整體結構圖
【Android 修煉手冊】系列內容 每周更新 歡迎關注下面賬號,獲取更新:
微信搜索公眾號: ZYLAB
Github
知乎
掘金
總結
以上是生活随笔為你收集整理的【Android 修炼手册】Gradle 篇 -- Gradle 源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 8421BCD码 5421BCD码 余三
- 下一篇: 如何完全删除sql2012_如何完全删除