命令构建gradle项目_【Android 修炼手册】Gradle 篇 -- Gradle 源码分析
預(yù)備知識
看完本文可以達(dá)到什么程度
閱讀前準(zhǔn)備工作
讀代碼的姿勢
目錄
本文主要從下面幾個(gè)部分進(jìn)行分析
一、Gradle 的啟動
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基本上可以看到,就是執(zhí)行了 gradle/wrapper/gradle-wrapper.jar 里的 org.gradle.wrapper.GradleWrapperMain,這樣我們就知道了,gradle 的入口類是 org.gradle.wrapper.GradleWrapperMain,也就知道代碼該從何開始看了。
先看 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); }這里就做了兩件事:
如果創(chuàng)建過多個(gè)項(xiàng)目的話,我們在 HOME/.gradle/wrapper/dists/ 里可以看到不同版本的 gradle wrapper,這也說明了我們之前最開始說的,為什么要使用 gradle wrapper 而不是直接在電腦里安裝 gradle,就是因?yàn)?gradle wrapper 會根據(jù)不同的項(xiàng)目下載不同版本的內(nèi)容,項(xiàng)目彼此之間互不影響。
基本上構(gòu)建過程就是分五步走,下面分別看這五個(gè)流程。
二、loadSettings
2.1 整體實(shí)現(xiàn)圖
2.2 具體分析
loadSettings 主要是加載 settings.gradle 文件,然后創(chuàng)建對應(yīng)的 project。
// DefaultGradleLauncher.loadSettings private void loadSettings() {if (stage == null) {buildListener.buildStarted(gradle);buildOperationExecutor.run(new LoadBuild());stage = Stage.Load;} }整體構(gòu)建流程:
2.2.1 調(diào)用 BuildListener.buildStarted() 回調(diào)接口
通知構(gòu)建開始。這個(gè)就是我們之前在 Gradle 基本使用 里說的生命周期回調(diào)。
2.2.2 執(zhí)行 init 腳本
調(diào)用鏈路
LoadBuild.run -> InitScriptHandler.executeScripts之前在 Gradle 基本使用 里說過 init.gradle 的作用,會在每個(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實(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在上一步找到 settings.gradle 文件以后,會以 settings.gradle 所在的同級目錄下,查找 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實(shí)現(xiàn)分析
這一步會讀取 gradle.properties 文件里的配置,系統(tǒng)配置,環(huán)境變量,以及命令行傳入的配置并存儲。
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實(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ì)說,因?yàn)樵诮馕?build.gradle 文件的時(shí)候也會用到這個(gè)方法。
下面是對應(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實(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)目,這樣就可以通過 project.getChildProjects 獲取項(xiàng)目的子項(xiàng)目了。
我們在寫 gradle 腳本的時(shí)候,經(jīng)常會用到的 project 屬性,就是在這個(gè)時(shí)候創(chuàng)建出來了。
到此為止,就解析了 settings.gradle 文件然后創(chuàng)建了項(xiàng)目實(shí)例。
三、configureBuild
3.1 整體實(shí)現(xiàn)圖
3.2 具體分析
我們之前有說到,gradle 構(gòu)建過程分為配置階段和運(yùn)行階段,配置階段主要是執(zhí)行腳本的內(nèi)容,運(yùn)行階段是執(zhí)行 task 的內(nèi)容,這里就是配置階段的流程。要注意,之前說的配置和運(yùn)行階段,是從整體來看的兩個(gè)階段,從源碼來理解,就是這篇文章介紹的幾個(gè)階段,要更細(xì)化一點(diǎn)。
配置階段執(zhí)行的內(nèi)容比較簡單,就是把 gradle 腳本編譯成 class 文件,然后運(yùn)行(gradle 是采用 groovy 語言編寫的,groovy 是一門 jvm 語言,所以必須要編譯成 class 才能運(yùn)行)。
在配置項(xiàng)目的時(shí)候,如果指定了 configure-on-demand 參數(shù),只會配置主項(xiàng)目以及執(zhí)行 task 需要的項(xiàng)目,默認(rèn)沒有指定,會配置所有的項(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實(shí)現(xiàn)分析
// TaskPathProjectEvaluator public void configureHierarchy(ProjectInternal project) {configure(project);for (Project sub : project.getSubprojects()) {configure((ProjectInternal) sub);} }最終執(zhí)行到了 LifecycleProjectEvaluator.doConfigure
3.2.2 回調(diào) BuildListener.beforeEvaluate 接口
在這里回調(diào) beforeEvaluate 接口,通知配置將要開始。我們也就知道了這個(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實(shí)現(xiàn)分析
在 PluginsProjectConfigureActions 里,會給 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實(shí)現(xiàn)分析
這里調(diào)用的還是 BuildOperationScriptPlugin.apply 去編譯和執(zhí)行 build.gradle 腳本,和前面解析 settings.gradle 是一樣的,這里我們先知道這個(gè)就是編譯 build.gradle 為 class。
文件并且執(zhí)行,然后先往后看流程,后面再詳細(xì)說腳本是如何編譯和執(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;} }4.2.1 處理需要排除的 task
調(diào)用鏈路
CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> ExcludedTaskFilteringBuildConfigurationAction.configure實(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(); }這一步是用來處理需要排除的 task,也就是在命令行通過 -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實(shí)現(xiàn)分析
這里會檢查命令行里是否有傳入 Task 名稱進(jìn)來,如果指定了要執(zhí)行的 task,那么什么都不做。
如果沒有指定,就看 project 是否有默認(rèn)的 task,默認(rèn)的 task 可以通過 defaultTasks 在 build.gradle 里進(jìn)行指定。
如果也默認(rèn) task 也沒有,那么就把要指定的 task 設(shè)置成 help task,也就是輸出 gradle 的幫助內(nèi)容。
4.2.3 計(jì)算 task 依賴圖
調(diào)用鏈路
CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> TaskNameResolvingBuildConfigurationAction.configure實(shí)現(xiàn)分析
4.2.4 生成 task graph
調(diào)用鏈路
CalculateTaskGraph.run -> TaskGraphExecuter.populate -> DefaultTaskExecutionPlan.determineExecutionPlan實(shí)現(xiàn)分析
根據(jù)上一步計(jì)算的 task 及其依賴,生成 task 圖
五、runTasks
5.1 整體實(shí)現(xiàn)圖
5.2 具體分析
task 圖生成以后,就開始執(zhí)行 task
5.2.1 處理 dry run
調(diào)用鏈路
DefaultBuildExecuter.execute -> DryRunBuildExecutionAction.execute實(shí)現(xiàn)分析
如果在命令行里指定了 --dry-run,在這里就會攔截 task 的執(zhí)行,直接輸出 task 的名稱以及執(zhí)行的先后關(guān)系。
5.2.2 創(chuàng)建線程,執(zhí)行 task
調(diào)用鏈路
DefaultBuildExecuter.execute -> SelectedTaskExecutionAction.execute -> DefaultTaskPlanExecutor.process實(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實(shí)現(xiàn)分析
到這里就正式開始 task 的執(zhí)行過程了。有幾個(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實(shí)現(xiàn)分析
經(jīng)過前面一系列處理,這里開始真正執(zhí)行 task 了。
這里可以看到,Task 的本質(zhì),其實(shí)就是執(zhí)行其中的 Actions。舉個(gè)例子來說,我們一般自定義 Task 的時(shí)候,經(jīng)常用下面的寫法:
task {doLast {// task 具體任務(wù)} }這里的 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; }這里邏輯不多,回調(diào)了 BuildListener.buildFinished 接口
通過上面幾個(gè)步驟,我們基本上看到了 gradle 的執(zhí)行流程,簡單來說,步驟如下:
七、gradle 腳本如何編譯和執(zhí)行
在前面介紹 loadsettings 和 configureBuild 階段的時(shí)候,我們提到了 BuildOperationScriptPlugin.apply 這個(gè)方法,只是簡單帶過,是用來編譯 gradle 腳本并執(zhí)行的,這里來具體分析一下。
7.1 編譯腳本
調(diào)用鏈路
BuildOperationScriptPlugin.apply -> DefaultScriptPluginFactory.ScriptPluginImpl.apply -> DefaultScriptCompilerFactory.ScriptCompilerImpl.compile -> BuildScopeInMemoryCachingScriptClassCompiler.compile -> CrossBuildInMemoryCachingScriptClassCache.getOrCompile -> FileCacheBackedScriptClassCompiler.compile實(shí)現(xiàn)分析
這里編譯過程分為兩部分,首先編譯腳本的 buildscript {} 部分,忽略其他部分,然后再編譯腳本的其他部分并執(zhí)行。所以 buildscript {} 里的內(nèi)容會先于其他內(nèi)容執(zhí)行。
腳本緩存路徑: /Users/zy/.gradle/caches/4.1/scripts-remapped/build_a3v29m9cbrge95ug6eejz9wuw/31f5shvfkfunwn5ullupyy7xt/cp_proj4dada6424967ba8dfea75e81c8880f7f/classes
目錄下的 class 如下:
3. 具體編譯方法是通過 RemappingScriptSource.getResource().getText() 獲取到腳本內(nèi)容,然后通過 GroovyClassLoader.parseClass 編譯的。
我們以 app/build.gradle 為例,看一下最終生成的腳本是什么樣子的。
build.gradle 腳本內(nèi)容
apply plugin: 'com.android.application' apply plugin: 'myplugin'android {compileSdkVersion 26defaultConfig {applicationId "com.zy.easygradle"minSdkVersion 19targetSdkVersion 26versionCode 1versionName "1.0"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility 1.8targetCompatibility 1.8}flavorDimensions "size", "color"productFlavors {big {dimension "size"}small {dimension "size"}blue {dimension "color"}red {dimension "color"}} }dependencies { // implementation gradleApi()implementation fileTree(dir: 'libs', include: ['*.jar'])implementation 'com.android.support:appcompat-v7:26.1.0'implementation 'com.android.support.constraint:constraint-layout:1.1.3'implementation project(':module1') }gradle.addBuildListener(new BuildListener() {@Overridevoid buildStarted(Gradle gradle) {// println('構(gòu)建開始')}@Overridevoid settingsEvaluated(Settings settings) {// println('settings 文件解析完成')}@Overridevoid projectsLoaded(Gradle gradle) {// println('項(xiàng)目加載完成')}@Overridevoid projectsEvaluated(Gradle gradle) {// println('項(xiàng)目解析完成')}@Overridevoid buildFinished(BuildResult result) {// println('構(gòu)建完成')} })gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {@Overridevoid beforeEvaluate(Project project) {// println("${project.name} 項(xiàng)目配置之前調(diào)用")}@Overridevoid afterEvaluate(Project project, ProjectState state) {// println("${project.name} 項(xiàng)目配置之后調(diào)用")} })gradle.taskGraph.whenReady {// println("task 圖構(gòu)建完成") } gradle.taskGraph.beforeTask {// println("task 執(zhí)行完成") } gradle.taskGraph.afterTask {// println("task 執(zhí)行完成") }task task1 {doLast {println('task2')} }task task2 {doLast {println('task2')} } task1.finalizedBy(task2)編譯后 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[]{"", ""})));} }可以看到,腳本類繼承自 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)用的方法對應(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)); }上面看到,task1 對應(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()));} }省略了一些內(nèi)容,可以看到,這個(gè)閉包的類繼承了 Closure,然后實(shí)現(xiàn)了 doCall 方法,在 doCall 方法里,調(diào)用了 doLast 方法,傳入了 _closure17 實(shí)例。這個(gè)就是腳本中的 task { doLast {} } 對應(yīng)的實(shí)現(xiàn)。
我們再看看 _closure17 的實(shí)現(xiàn)。
同樣也是繼承了 Closure,在 doCall 方法里調(diào)用了 println,這正是我們在 task 的里執(zhí)行的任務(wù),也就是前面提到的 task 的 actions。
這里我們再理順一下,每一個(gè) build.gradle 腳本,對應(yīng)一個(gè)繼承了 ProjectScript 的類,每一個(gè)閉包,對應(yīng)了一個(gè)繼承自 Closure 的類
7.2 調(diào)用腳本 run 方法
接著就是執(zhí)行腳本類的 run 方法,也就是我們在上面分析的 run 方法。
其中強(qiáng)調(diào)的一點(diǎn)是,run 方法里對 task 的創(chuàng)建,僅僅是執(zhí)行了 task.doCall,這也就是為什么配置階段不會執(zhí)行 task 任務(wù),但會執(zhí)行 task 閉包里的內(nèi)容。
八、插件調(diào)用流程
之前在 Gradle的基本使用 里講到過自定義插件,使用的時(shí)候是通過 apply plugin 'xxx' 來使用的,具體的調(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最后的 Plugin.apply 就調(diào)用到插件里實(shí)現(xiàn)的 apply() 函數(shù)了
九、總結(jié)
整體結(jié)構(gòu)圖
【Android 修煉手冊】系列內(nèi)容 每周更新 歡迎關(guān)注下面賬號,獲取更新:
微信搜索公眾號: ZYLAB
Github
知乎
ZY5A59 的個(gè)人主頁 - 掘金
總結(jié)
以上是生活随笔為你收集整理的命令构建gradle项目_【Android 修炼手册】Gradle 篇 -- Gradle 源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dede服务器建站_建站就是这么简单(内
- 下一篇: python读取 application