一、前言
上一篇文章中我们讲到了Android Plugin中的apply方法中的回调方法configureExtension
,详见Android Plugin源码与Gradle构建(二)。今天我们继续分析Android Plugin的源码,主要分析最后一个回调方法createTasks
的逻辑。
二、初识Task
task就是一个任务,gradle构建都是由一个个任务组成的。我们可以这样定义一个task:
task myTask {
println "this is a task"
}
task有自己的属性,也有自己的生命周期,它的生命周期分为三个阶段,分别为初始化阶段、配置阶段和执行阶段,如下图所示(图片来源:掌握构建生命周期):
![](https://img.haomeiwen.com/i7025056/4d0252014ff20da0.png)
上面我们定义的myTask
任务只是在配置阶段,配置阶段的代码只要执行任何task,它都会跟着执行。所以,我们执行一下gradle clean任务,this is a task
也被打印出来了。
如果我们希望定义的任务只能在执行阶段才执行的话,那么我们可以将实现逻辑放到doFirst、doLast中。例如看下面一个例子:
task myTask {
doLast {
println 'myTask do Last1'
}
}
myTask.doFirst {
println 'myTask do First2'
}
myTask.doFirst {
println 'myTask do First1'
}
myTask.doLast {
println 'myTask do Last2'
}
当我们执行gradle myTask
时,就会打印如下内容:
> Task :app:myTask
myTask do First1
myTask do First2
myTask do Last1
myTask do Last2
我们可以理解为task中包含一个队列,队列中包含的是要执行的Action,当使用doFirst
时,即往队列首加入了一个Action;当使用doLast
时,即往队列尾加入了一个Action。所以myTask使用了两次doFirst
时,最后一次被加入了队列首;myTask使用了两次doLast
时,最后一次被加入了队列尾。
task不仅仅有生命周期的概念,同时也具有“继承”的概念。这个“继承”是通过关键字dependsOn
来实现的,它和类的继承不是一回事,我们先看下面的这个例子:
task task1 << {
println 'task1'
}
task task2 << {
println 'task2'
}
task task3 << {
println 'task3'
}
task task4 << {
println 'task4'
}
task1.dependsOn('task2')
task2.dependsOn('task3')
task1.dependsOn('task4')
我们定义了四个task,并且确定了“继承”关系:task1->task2/task4->task3。
执行gradle task1
,打印的内容是:
> Task :app:task3
task3
> Task :app:task2
task2
> Task :app:task4
task4
> Task :app:task1
task1
可以看到是task3
先执行,然后是task2
,接着是task4
,最后才是task1
。这是因为dependensOn的逻辑是执行“高”辈分的,然后再执行“低”辈分的。
由于“继承”关系是task1->task2/task4->task3。所以task3
是最高级的,task2
和task4
平级,task1
是最低级的。所以会先执行task3
,然后执行task3
的上一级,即task2
,然后执行和task3
没有联系的task4
,最后才执行task1
。
task的基础知识先介绍到这里,由于本文篇幅重点在于介绍Android Plugin源码的Task任务,如果对task感兴趣可以去看一下《Android+Gradle权威指南》或者官方文档。
三、Android插件的Task
上一篇文章我们说到了createExtension回调方法,现在我们来看createTasks的回调方法:
private void createTasks() {
//创建了关于Apk卸载、设备检查等一些方法
threadRecorder.record(
ExecutionType.TASK_MANAGER_CREATE_TASKS,
project.getPath(),
null,
() -> taskManager.createTasksBeforeEvaluate());
//创建和Android相关的一些重要任务
project.afterEvaluate(
project ->
threadRecorder.record(
ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
project.getPath(),
null,
() -> createAndroidTasks(false)));
}
主要是调用了两个方法,一个是taskManager. createTasksBeforeEvaluate
,一个是createAndroidTasks
,其中和Android相关的Task的创建都在createAndroidTasks
方法中。
@VisibleForTesting
final void createAndroidTasks(boolean force) {
// Make sure unit tests set the required fields.
checkState(extension.getBuildToolsRevision() != null,
"buildToolsVersion is not specified.");
checkState(extension.getCompileSdkVersion() != null, "compileSdkVersion is not specified.");
ndkHandler.setCompileSdkVersion(extension.getCompileSdkVersion());
// get current plugins and look for the default Java plugin.
if (project.getPlugins().hasPlugin(JavaPlugin.class)) {
throw new BadPluginException(
"The 'java' plugin has been applied, but it is not compatible with the Android plugins.");
}
boolean targetSetupSuccess = ensureTargetSetup();
sdkHandler.ensurePlatformToolsIsInstalledWarnOnFailure(
extraModelInfo.getSyncIssueHandler());
// Stop trying to configure the project if the SDK is not ready.
// Sync issues will already have been collected at this point in sync.
if (!targetSetupSuccess) {
project.getLogger()
.warn("Aborting configuration as SDK is missing components in sync mode.");
return;
}
// don't do anything if the project was not initialized.
// Unless TEST_SDK_DIR is set in which case this is unit tests and we don't return.
// This is because project don't get evaluated in the unit test setup.
// See AppPluginDslTest
if (!force
&& (!project.getState().getExecuted() || project.getState().getFailure() != null)
&& SdkHandler.sTestSdkFolder == null) {
return;
}
if (hasCreatedTasks) {
return;
}
hasCreatedTasks = true;
extension.disableWrite();
taskManager.configureCustomLintChecks();
ProcessProfileWriter.getProject(project.getPath())
.setCompileSdk(extension.getCompileSdkVersion())
.setBuildToolsVersion(extension.getBuildToolsRevision().toString())
.setSplits(AnalyticsUtil.toProto(extension.getSplits()));
String kotlinPluginVersion = getKotlinPluginVersion();
if (kotlinPluginVersion != null) {
ProcessProfileWriter.getProject(project.getPath())
.setKotlinPluginVersion(kotlinPluginVersion);
}
// setup SDK repositories.
sdkHandler.addLocalRepositories(project);
threadRecorder.record(
ExecutionType.VARIANT_MANAGER_CREATE_ANDROID_TASKS,
project.getPath(),
null,
() -> {
variantManager.createAndroidTasks();
ApiObjectFactory apiObjectFactory =
new ApiObjectFactory(
androidBuilder,
extension,
variantFactory,
project.getObjects());
for (VariantScope variantScope : variantManager.getVariantScopes()) {
BaseVariantData variantData = variantScope.getVariantData();
apiObjectFactory.create(variantData);
}
// Make sure no SourceSets were added through the DSL without being properly configured
sourceSetManager.checkForUnconfiguredSourceSets();
// must run this after scopes are created so that we can configure kotlin
// kapt tasks
taskManager.addDataBindingDependenciesIfNecessary(
extension.getDataBinding(), variantManager.getVariantScopes());
});
// create the global lint task that depends on all the variants
taskManager.configureGlobalLintTask(variantManager.getVariantScopes());
// Create and read external native build JSON files depending on what's happening right
// now.
//
// CREATE PHASE:
// Creates JSONs by shelling out to external build system when:
// - Any one of AndroidProject.PROPERTY_INVOKED_FROM_IDE,
// AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED,
// AndroidProject.PROPERTY_BUILD_MODEL_ONLY,
// AndroidProject.PROPERTY_REFRESH_EXTERNAL_NATIVE_MODEL are set.
// - *and* AndroidProject.PROPERTY_REFRESH_EXTERNAL_NATIVE_MODEL is set
// or JSON files don't exist or are out-of-date.
// Create phase may cause ProcessException (from cmake.exe for example)
//
// READ PHASE:
// Reads and deserializes JSONs when:
// - Any one of AndroidProject.PROPERTY_INVOKED_FROM_IDE,
// AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED,
// AndroidProject.PROPERTY_BUILD_MODEL_ONLY,
// AndroidProject.PROPERTY_REFRESH_EXTERNAL_NATIVE_MODEL are set.
// Read phase may produce IOException if the file can't be read for standard IO reasons.
// Read phase may produce JsonSyntaxException in the case that the content of the file is
// corrupt.
boolean forceRegeneration =
projectOptions.get(BooleanOption.IDE_REFRESH_EXTERNAL_NATIVE_MODEL);
checkSplitConfiguration();
if (ExternalNativeBuildTaskUtils.shouldRegenerateOutOfDateJsons(projectOptions)) {
threadRecorder.record(
ExecutionType.VARIANT_MANAGER_EXTERNAL_NATIVE_CONFIG_VALUES,
project.getPath(),
null,
() -> {
for (VariantScope variantScope : variantManager.getVariantScopes()) {
ExternalNativeJsonGenerator generator =
variantScope.getExternalNativeJsonGenerator();
if (generator != null) {
// This will generate any out-of-date or non-existent JSONs.
// When refreshExternalNativeModel() is true it will also
// force update all JSONs.
generator.build(forceRegeneration);
}
}
});
}
BuildableArtifactImpl.Companion.enableResolution();
}
代码有点长,我们挑重点的看,其实上面的代码最主要是执行了回调方法:
variantManager.createAndroidTasks();
ApiObjectFactory apiObjectFactory =
new ApiObjectFactory(
androidBuilder,
extension,
variantFactory,
project.getObjects());
for (VariantScope variantScope : variantManager.getVariantScopes()) {
BaseVariantData variantData = variantScope.getVariantData();
apiObjectFactory.create(variantData);
}
// Make sure no SourceSets were added through the DSL without being properly configured
sourceSetManager.checkForUnconfiguredSourceSets();
// must run this after scopes are created so that we can configure kotlin
// kapt tasks
taskManager.addDataBindingDependenciesIfNecessary(
extension.getDataBinding(), variantManager.getVariantScopes());
其中执行了
public void createAndroidTasks() {
variantFactory.validateModel(this);
variantFactory.preVariantWork(project);
//当variantScopes为空,则创建variantScopes
if (variantScopes.isEmpty()) {
recorder.record(
ExecutionType.VARIANT_MANAGER_CREATE_VARIANTS,
project.getPath(),
null /*variantName*/,
this::populateVariantDataList);
}
// Create top level test tasks.
recorder.record(
ExecutionType.VARIANT_MANAGER_CREATE_TESTS_TASKS,
project.getPath(),
null /*variantName*/,
() -> taskManager.createTopLevelTestTasks(!productFlavors.isEmpty()));
//为所有定义的渠道创建相关的构建任务
for (final VariantScope variantScope : variantScopes) {
recorder.record(
ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT,
project.getPath(),
variantScope.getFullVariantName(),
() -> createTasksForVariantData(variantScope));
}
taskManager.createReportTasks(variantScopes);
}
我们直接看为所有的定义的渠道创建相关的构建任务的逻辑,即调用了variantScopes
的foreach
方法,对于每一个渠道,相应的执行了createTasksForVariantData
方法:
public void createTasksForVariantData(final VariantScope variantScope) {
//1、解析variant渠道等信息
final BaseVariantData variantData = variantScope.getVariantData();
final VariantType variantType = variantData.getType();
final GradleVariantConfiguration variantConfig = variantScope.getVariantConfiguration();
final BuildTypeData buildTypeData = buildTypes.get(variantConfig.getBuildType().getName());
if (buildTypeData.getAssembleTask() == null) {
//2、创建构建类型(有可能是自定义的buildType)的assembleTask
buildTypeData.setAssembleTask(taskManager.createAssembleTask(buildTypeData));
}
// 3、给assemble添加依赖,即assemble任务需要依赖于上面创建的构建类型的assembleTask
taskManager
.getTaskFactory()
.configure(
"assemble",
task -> {
assert buildTypeData.getAssembleTask() != null;
task.dependsOn(buildTypeData.getAssembleTask().getName());
});
//4、创建该variant专属的assembleTask
createAssembleTaskForVariantData(variantData);
if (variantType.isForTesting()) {
//省略...
} else {
//5、给assembleTask添加构建项目所需task依赖
taskManager.createTasksForVariantScope(variantScope);
}
}
createTasksForVariantData
方法主要执行了以下逻辑:
1、解析variant渠道等信息
2、创建构建类型(在buildTypes标签下定义的构建类型)的assembleTask
3、给assemble添加依赖
4、创建该variant专属的assembleTask
5、给assembleTask添加构建项目所需task依赖
首先看一下第四步的详细逻辑:
private void createAssembleTaskForVariantData(final BaseVariantData variantData) {
final VariantScope variantScope = variantData.getScope();
if (variantData.getType().isForTesting()) {
variantScope.setAssembleTask(taskManager.createAssembleTask(variantData));
} else {
BuildTypeData buildTypeData =
buildTypes.get(variantData.getVariantConfiguration().getBuildType().getName());
Preconditions.checkNotNull(buildTypeData.getAssembleTask());
if (productFlavors.isEmpty()) {
//如果没有配置渠道
} else {
//省略部分代码..
// assembleTask for this flavor(dimension), created on demand if needed.
if (variantConfig.getProductFlavors().size() > 1) {
//获取渠道名
final String name = StringHelper.capitalize(variantConfig.getFlavorName());
//组装名字
final String variantAssembleTaskName =
StringHelper.appendCapitalized("assemble", name);
if (!taskManager.getTaskFactory().containsKey(variantAssembleTaskName)) {
//创建相应渠道任务
Task task = taskManager.getTaskFactory().create(variantAssembleTaskName);
task.setDescription("Assembles all builds for flavor combination: " + name);
//设置该渠道任务属于哪个组
task.setGroup("Build");
task.dependsOn(variantScope.getAssembleTask().getName());
}
//assemble依赖该渠道任务
taskManager
.getTaskFactory()
.configure(
"assemble", task1 -> task1.dependsOn(variantAssembleTaskName));
}
}
}
}
这里主要组装了任务的名字,然后创建了相应的渠道任务。该渠道任务现在已经创建出来了,但是当我们执行该渠道任务时,如果最终能构建出一个apk,则必须需要依赖其他一些task,下面我们讲到第五步:
public void createTasksForVariantScope(@NonNull final VariantScope variantScope) {
BaseVariantData variantData = variantScope.getVariantData();
assert variantData instanceof ApplicationVariantData;
createAnchorTasks(variantScope);
createCheckManifestTask(variantScope);
handleMicroApp(variantScope);
// Create all current streams (dependencies mostly at this point)
createDependencyStreams(variantScope);
// Add a task to publish the applicationId.
createApplicationIdWriterTask(variantScope);
taskFactory.create(new MainApkListPersistence.ConfigAction(variantScope));
taskFactory.create(new BuildArtifactReportTask.ConfigAction(variantScope));
// Add a task to process the manifest(s)
recorder.record(
ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK,
project.getPath(),
variantScope.getFullVariantName(),
() -> createMergeApkManifestsTask(variantScope));
// Add a task to create the res values
recorder.record(
ExecutionType.APP_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK,
project.getPath(),
variantScope.getFullVariantName(),
() -> createGenerateResValuesTask(variantScope));
省略类似方法...
}
在这个方法中创建了大量构建时的task,例如创建Manifest文件,合并Manifest文件、处理resource文件等task。所以当我们执行了assemble任务时,其实上面创建的task也一并被执行,所有这些task构成了项目的一次构建。
四、总结
Android Plugin的源码分析到这里暂告一段落了。从Android Plugin源码中我们知道了Android项目的整个构建过程,并且了解了如何创建Extension和Task,这为后面自定义插件的创建打下了基础。
网友评论