【好文推荐】Gradle-源码全解析,面试完腾讯我才发现这些知识点竟然没掌握全

SettingsLocation settingsLocation,
ClassLoaderScope buildRootClassLoaderScope,
StartParameter startParameter) {
Timer settingsProcessingClock = Timers.startTimer();
Map<String, String> properties = propertiesLoader.mergeProperties(Collections.<String, String>emptyMap());
SettingsInternal settings = settingsFactory.createSettings(gradle, settingsLocation.getSettingsDir(),
settingsLocation.getSettingsScriptSource(), properties, startParameter, buildRootClassLoaderScope);
applySettingsScript(settingsLocation, settings);
LOGGER.debug(“Timing: Processing settings took: {}”, settingsProcessingClock.getElapsed());
return settings;
}

private void applySettingsScript(SettingsLocation settingsLocation, final SettingsInternal settings) {
ScriptSource settingsScriptSource = settingsLocation.getSettingsScriptSource();
ClassLoaderScope settingsClassLoaderScope = settings.getClassLoaderScope();
ScriptHandler scriptHandler = scriptHandlerFactory.create(settingsScriptSource, settingsClassLoaderScope);
ScriptPlugin configurer = configurerFactory.create(settingsScriptSource, scriptHandler, settingsClassLoaderScope, settings.getRootClassLoaderScope(), true);
configurer.apply(settings);
}

2.2.7 创建 project 以及 subproject

调用链路

LoadBuild.run -> NotifyingSettingsLoader.findAndLoadSettings -> CompositeBuildSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findAndLoadSettings -> DefaultSettingsLoader.findSettingsAndLoadIfAppropriate -> NotifyingSettingsProcessor.process -> ProjectPropertySettingBuildLoader.load -> InstantiatingBuildLoader.load

实现分析
在解析了 settings.gradle 文件以后,就可以知道项目里有哪些 project,就可以创建 project 实例了。

// InstantiatingBuildLoader
// 这里传入的参数对应的是:rootProjectDescriptor: SettingsInternal.getRootProject() defaultProject: SettingsInternal.getDefaultProject() buildRootClassLoaderScope:SettingsInternal.getRootClassLoaderScope()
public void load(ProjectDescriptor rootProjectDescriptor, ProjectDescriptor defaultProject, GradleInternal gradle, ClassLoaderScope buildRootClassLoaderScope) {
createProjects(rootProjectDescriptor, gradle, buildRootClassLoaderScope);
attachDefaultProject(defaultProject, gradle);
}

private void attachDefaultProject(ProjectDescriptor defaultProject, GradleInternal gradle) {
gradle.setDefaultProject(gradle.getRootProject().getProjectRegistry().getProject(defaultProject.getPath()));
}

private void createProjects(ProjectDescriptor rootProjectDescriptor, GradleInternal gradle, ClassLoaderScope buildRootClassLoaderScope) {
// 创建主项目实例
// ProjectInternal 继承自 Project,最终返回的 rootProject 是 DefaultProject 类型
ProjectInternal rootProject = projectFactory.createProject(rootProjectDescriptor, null, gradle, buildRootClassLoaderScope.createChild(“root-project”), buildRootClassLoaderScope);
gradle.setRootProject(rootProject);
addProjects(rootProject, rootProjectDescriptor, gradle, buildRootClassLoaderScope);
}

private void addProjects(ProjectInternal parent, ProjectDescriptor parentProjectDescriptor, GradleInternal gradle, ClassLoaderScope buildRootClassLoaderScope) {
// 创建子项目实例
for (ProjectDescriptor childProjectDescriptor : parentProjectDescriptor.getChildren()) {
ProjectInternal childProject = projectFactory.createProject(childProjectDescriptor, parent, gradle, parent.getClassLoaderScope().createChild(“project-” + childProjectDescriptor.getName()), buildRootClassLoaderScope);
addProjects(childProject, childProjectDescriptor, gradle, buildRootClassLoaderScope);
}
}

// ProjectFactory
public DefaultProject createProject(ProjectDescriptor projectDescriptor, ProjectInternal parent, GradleInternal gradle, ClassLoaderScope selfClassLoaderScope, ClassLoaderScope baseClassLoaderScope) {
// 获取 project 对应的 build.gradle
File buildFile = projectDescriptor.getBuildFile();
ScriptSource source = UriScriptSource.file(“build file”, buildFile);
// 创建 project 实例
DefaultProject project = instantiator.newInstance(DefaultProject.class,
projectDescriptor.getName(),
parent,
projectDescriptor.getProjectDir(),
source,
gradle,
gradle.getServiceRegistryFactory(),
selfClassLoaderScope,
baseClassLoaderScope
);

// 设置 project 的层级关系
if (parent != null) {
parent.addChildProject(project);
}
// 注册 project
projectRegistry.addProject(project);

return project;
}

这里根据 settings.gradle 的配置,创建项目实例。创建子项目的时候,如果父项目不为空,就将自己设置成父项目的子项目,这样就可以通过 project.getChildProjects 获取项目的子项目了。
我们在写 gradle 脚本的时候,经常会用到的 project 属性,就是在这个时候创建出来了。

到此为止,就解析了 settings.gradle 文件然后创建了项目实例。

三、configureBuild

3.1 整体实现图

3.2 具体分析

我们之前有说到,gradle 构建过程分为配置阶段和运行阶段,配置阶段主要是执行脚本的内容,运行阶段是执行 task 的内容,这里就是配置阶段的流程。要注意,之前说的配置和运行阶段,是从整体来看的两个阶段,从源码来理解,就是这篇文章介绍的几个阶段,要更细化一点。
配置阶段执行的内容比较简单,就是把 gradle 脚本编译成 class 文件,然后运行(gradle 是采用 groovy 语言编写的,groovy 是一门 jvm 语言,所以必须要编译成 class 才能运行)。

// DefaultGradleLauncher
private void configureBuild() {
if (stage == Stage.Load) {
buildOperationExecutor.run(new ConfigureBuild());

stage = Stage.Configure;
}
}

在配置项目的时候,如果指定了 configure-on-demand 参数,只会配置主项目以及执行 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);
}
}

最终执行到了 LifecycleProjectEvaluator.doConfigure

3.2.2 回调 BuildListener.beforeEvaluate 接口

在这里回调 beforeEvaluate 接口,通知配置将要开始。我们也就知道了这个回调执行的阶段。

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 编译脚本并执行

调用链路

ConfigureBuild.run -> DefaultBuildConfigurer.configure -> TaskPathProjectEvaluator.configureHierarchy -> TaskPathProjectEvaluator.configure -> DefaultProject.evaluate -> LifecycleProjectEvaluator.evaluate -> LifecycleProjectEvaluator.doConfigure -> ConfigureActionsProjectEvaluator.evaluate -> BuildScriptProcessor.execute -> BuildOperationScriptPlugin.apply

实现分析
这里调用的还是 BuildOperationScriptPlugin.apply 去编译和执行 build.gradle 脚本,和前面解析 settings.gradle 是一样的,这里我们先知道这个就是编译 build.gradle 为 class。
文件并且执行,然后先往后看流程,后面再详细说脚本是如何编译和执行的。

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 excludedTaskNames = gradle.getStartParameter().getExcludedTaskNames();
if (!excludedTaskNames.isEmpty()) {
final Set<Spec> filters = new HashSet<Spec>();
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 名称进来,如果指定了要执行的 task,那么什么都不做。
如果没有指定,就看 project 是否有默认的 task,默认的 task 可以通过 defaultTasks 在 build.gradle 里进行指定。
如果也默认 task 也没有,那么就把要指定的 task 设置成 help task,也就是输出 gradle 的帮助内容。

4.2.3 计算 task 依赖图

调用链路

CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> TaskNameResolvingBuildConfigurationAction.configure

实现分析

  1. 根据命令行的 taskname 筛选 task。如果我们的 task 指定了 project,也就是类似这样的 :app:assembleDebug,那么就直接选中了 task,如果没有指定具体 project,那么会把所有 project 下符合 taskname 的 task 都筛选出来。

CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> TaskNameResolvingBuildConfigurationAction.configure -> CommandLineTaskParser.parseTasks

  1. 把 task 添加到 taskGraph 中,这里会处理 task 的依赖关系,包括 dependson finalizedby mustrunafter shouldrunafter,然后把信息都保存在 org.gradle.execution.taskgraph.TaskInfo 里。

CalculateTaskGraph.run -> DefaultBuildConfigurationActionExecuter.select -> TaskNameResolvingBuildConfigurationAction.configure -> DefaultTaskGraphExecuter.addTasks

4.2.4 生成 task graph

调用链路

CalculateTaskGraph.run -> TaskGraphExecuter.populate -> DefaultTaskExecutionPlan.determineExecutionPlan

实现分析
根据上一步计算的 task 及其依赖,生成 task 图

五、runTasks

5.1 整体实现图

5.2 具体分析

task 图生成以后,就开始执行 task

5.2.1 处理 dry run

调用链路

DefaultBuildExecuter.execute -> DryRunBuildExecutionAction.execute

实现分析
如果在命令行里指定了 --dry-run,在这里就会拦截 task 的执行,直接输出 task 的名称以及执行的先后关系。

5.2.2 创建线程,执行 task

调用链路

DefaultBuildExecuter.execute -> SelectedTaskExecutionAction.execute -> DefaultTaskPlanExecutor.process

实现分析
创建 TaskExecutorWorker 去执行 task,默认是 8 个线程。

// DefaultTaskPlanExecutor
public void process(TaskExecutionPlan taskExecutionPlan, Action<? super TaskInternal> taskWorker) {
ManagedExecutor executor = executorFactory.create(“Task worker for '” + taskExecutionPlan.getDisplayName() + “'”);
try {
WorkerLease parentWorkerLease = workerLeaseService.getCurrentWorkerLease();
// 开线程
startAdditionalWorkers(taskExecutionPlan, taskWorker, executor, parentWorkerLease);
taskWorker(taskExecutionPlan, taskWorker, parentWorkerLease).run();
taskExecutionPlan.awaitCompletion();
} finally {
executor.stop();
}
}

5.2.3 task 执行前处理

调用链路

DefaultBuildExecuter.execute -> SelectedTaskExecutionAction.execute -> DefaultTaskPlanExecutor.process -> TaskExecutorWorker.run -> DefaultTaskExecutionPlan.executeWithTask -> DefaultTaskExecutionPlan.selectNextTask -> DefaultTaskExecutionPlan.processTask -> EventFiringTaskWorker.execute -> DefaultBuildOperationExecutor.run

实现分析
到这里就正式开始 task 的执行过程了。有几个步骤:

  1. 回调 TaskExecutionListener.beforeExecute
  2. 链式执行一些列对 Task 的处理,具体的处理如下:

CatchExceptionTaskExecuter.execute // 加了 try catch,防止执行过程中异常
ExecuteAtMostOnceTaskExecuter.execute // 判断 task 是否执行过
SkipOnlyIfTaskExecuter.execute // 判断 task 的 onlyif 条件是否满足执行
SkipTaskWithNoActionsExecuter.execute // 跳过没有 action 的 task,没有 action 说明 task 不需要执行
ResolveTaskArtifactStateTaskExecuter.execute // 设置 artifact 状态
SkipEmptySourceFilesTaskExecuter.execute // 跳过设置了 source file 但是 source file 为空的 task,source file 为空说明 task 没有需要处理的资源
ValidatingTaskExecuter.execute() // 确认 task 是否可以执行
ResolveTaskOutputCachingStateExecuter.execute // 处理 task output 缓存
SkipUpToDateTaskExecuter.execute // 跳过 update-to-date 的 task
ExecuteActionsTaskExecuter.execute // 真正执行 task

5.2.4 task 执行

调用链路

DefaultBuildExecuter.execute -> SelectedTaskExecutionAction.execute -> DefaultTaskPlanExecutor.process -> TaskExecutorWorker.run -> DefaultTaskExecutionPlan.executeWithTask -> DefaultTaskExecutionPlan.selectNextTask -> DefaultTaskExecutionPlan.processTask -> EventFiringTaskWorker.execute -> DefaultBuildOperationExecutor.run -> ExecuteActionsTaskExecuter.execute

实现分析
经过前面一系列处理,这里开始真正执行 task 了。

  1. 回调 TaskActionListener.beforeActions
  2. 回调 OutputsGenerationListener.beforeTaskOutputsGenerated
  3. 取出 task 中的 Actions 全部执行

// ExecuteActionsTaskExecuter
private GradleException executeActions(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) {
final List actions = new ArrayList(task.getTaskActions());
int actionNumber = 1;
for (ContextAwareTaskAction action : actions) {
// …
executeAction("Execute task action " + actionNumber + “/” + actions.size() + " for " + task.getPath(), task, action, context);
// …
actionNumber++;
}
return null;
}

这里可以看到,Task 的本质,其实就是执行其中的 Actions。举个例子来说,我们一般自定义 Task 的时候,经常用下面的写法:

task {
doLast {
// task 具体任务
}
}

这里的 doLast 就相当于给 Task 添加了一个 Action。
看一下 AbstractTask 的 doLast 方法

// AbstractTask
public Task doLast(final Action<? super Task> action) {
// …
taskMutator.mutate(“Task.doLast(Action)”, new Runnable() {
public void run() {
getTaskActions().add(wrap(action));
}
});
return this;
}

private ContextAwareTaskAction wrap(final Action<? super Task> action) {
if (action instanceof ContextAwareTaskAction) {
return (ContextAwareTaskAction) action;
}
return new TaskActionWrapper(action);
}

可以看到,我们传入的闭包,最终是包装成 TaskActionWrapper 添加到 task 的 actions 中的。

  1. 回调 TaskActionListener.afterActions
  2. 回调 TaskExecutionListener.afterExecute

六、finishBuild

6.1 整体实现图

image.png

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 的执行流程,简单来说,步骤如下:

  1. 解析 settings.gradle 并执行,生成 Project 实例
  2. 解析 build.gradle 并执行
  3. 生成 task 依赖图
  4. 执行 task

七、gradle 脚本如何编译和执行

在前面介绍 loadSettings 和 configureBuild 阶段的时候,我们提到了 BuildOperationScriptPlugin.apply 这个方法,只是简单带过,是用来编译 gradle 脚本并执行的,这里来具体分析一下。

7.1 编译脚本

调用链路

BuildOperationScriptPlugin.apply -> DefaultScriptPluginFactory.ScriptPluginImpl.apply -> DefaultScriptCompilerFactory.ScriptCompilerImpl.compile -> BuildScopeInMemoryCachingScriptClassCompiler.compile -> CrossBuildInMemoryCachingScriptClassCache.getOrCompile -> FileCacheBackedScriptClassCompiler.compile

实现分析
这里编译过程分为两部分,首先编译脚本的 buildscript {} 部分,忽略其他部分,然后再编译脚本的其他部分并执行。所以 buildscript {} 里的内容会先于其他内容执行

  1. 会先检查缓存,如果有缓存的话,直接使用,没有缓存再进行编译

  2. 最终会调用到 CompileToCrossBuildCacheAction.execute -> DefaultScriptCompilationHandler.compileToDir -> DefaultScriptCompilationHandler.compileScript 去执行真正的编译操作
    脚本缓存路径: /Users/zy/.gradle/caches/4.1/scripts-remapped/build_a3v29m9cbrge95ug6eejz9wuw/31f5shvfkfunwn5ullupyy7xt/cp_proj4dada6424967ba8dfea75e81c8880f7f/classes
    目录下的 class 如下:

  3. 具体编译方法是通过 RemappingScriptSource.getResource().getText() 获取到脚本内容,然后通过 GroovyClassLoader.parseClass 编译的。
    我们以 app/build.gradle 为例,看一下最终生成的脚本是什么样子的。
    build.gradle 脚本内容

apply plugin: ‘com.android.application’
apply plugin: ‘myplugin’

android {
compileSdkVersion 26
defaultConfig {
applicationId “com.zy.easygradle”
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName “1.0”
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 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() {
@Override
void buildStarted(Gradle gradle) {
// println(‘构建开始’)
}

@Override
void settingsEvaluated(Settings settings) {
// println(‘settings 文件解析完成’)
}

@Override
void projectsLoaded(Gradle gradle) {
// println(‘项目加载完成’)
}

@Override
void projectsEvaluated(Gradle gradle) {
// println(‘项目解析完成’)
}

@Override
void buildFinished(BuildResult result) {
// println(‘构建完成’)
}
})

gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后送福利了,现在关注我并且加入群聊可以获取包含源码解析,自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,欢迎加群探讨,有flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿

点击GitHub领取
录播视频图.png

img-wXuwt2Ua-1710677690704)]
[外链图片转存中…(img-XYbpm2UY-1710677690704)]
[外链图片转存中…(img-PztcqwXi-1710677690705)]
[外链图片转存中…(img-mYXFj7LG-1710677690705)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-mM09PbFy-1710677690705)]

最后送福利了,现在关注我并且加入群聊可以获取包含源码解析,自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,欢迎加群探讨,有flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿

点击GitHub领取
[外链图片转存中…(img-mFMIR7mK-1710677690706)]

  • 16
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值