gradle android 源码,Android Gradle Plugin源码分析

本文由玉刚说写作平台提供写作赞助,版权归玉刚说微信公众号所有

原作者:ShinyZeng

版权声明:未经玉刚说许可,不得以任何形式转载

前言:这篇文章在一个月之前已经发布到玉刚说微信公众号。Gradle 这一块对于我们Android开发来说,一直是比较无人问津的一块,平日的开发中也比较少涉及,即使涉及也是网上查查资料就可以解决,并不会深入理解原理,即使深入了,过一段时间不接触也容易忘记,譬如我今日再看这篇文章,就发现有很多已经忘记了...真是汗颜啊...所以简书中再发出来,做个备忘吧。

一、源码依赖

本文基于:

android gradle plugin版本:com.android.tools.build:gradle:2.3.0

gradle 版本:4.1

Gradle源码总共30个G,为简单起见,方便大家看源码,此处通过gradle依赖的形式来查看源码,依赖源码姿势:

创建一个新工程,app 项目目录中删除所有文件,仅留下gradle文件,依赖

apply plugin: 'java'

sourceCompatibility = 1.8

dependencies {

compile gradleApi()

compile 'com.android.tools.build:gradle:2.3.0'

}

11f030b2034f

将跟目录下的gradle文件,删除掉gradle依赖

buildscript {

repositories {

google()

jcenter()

}

dependencies {

// compile 'com.android.tools.build:gradle:2.3.0'

}

}

然后rebuild一下,就可以在External Libraries中查看到android gradle的源码已经依赖了

11f030b2034f

二、Android Gradle Plugin简介

我们知道Android gradle plugin是用来构建Android工程的gradle插件,在Android gradle 插件中,可以看到app工程和library工程所依赖的plugin是不一样的

// app 工程

apply plugin: 'com.android.application'

// library 工程

apply plugin: 'com.android.library'

而对应填写andorid块中所填写的配置也不同,这就是区分Application和Library的插件的extension块

分别为:

app工程 -> AppPlugin -> AppExtension

librar工程 -> LibraryPlugin -> LibraryExtension

对应的是AppPlugin和AppExtension,这两个插件构建的流程大抵是相同的,只是各自插件生成的任务不同,接下来我们着重分析Application插件是如何构建我们的Android应用的

三、AppPlugin的构建流程

我们先看下app工程中gradle的文件格式

apply plugin: 'com.android.application'

android {

compileSdkVersion 25

buildToolsVersion '26.0.2'

defaultConfig {

applicationId "com.zengshaoyi.gradledemo"

minSdkVersion 15

targetSdkVersion 25

versionCode project.ext.versionCode

versionName project.ext.versionName

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {

release {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

}

lintOptions {

abortOnError false

}

}

跟踪apply方法,其实是进入到

AppPlugin的apply的方法,我们可以看到内部实现是直接调用父类BasePlugin的apply方法

protected void apply(@NonNull Project project) {

checkPluginVersion();

this.project = project;

ExecutionConfigurationUtil.setThreadPoolSize(project);

checkPathForErrors();

checkModulesForErrors();

ProfilerInitializer.init(project);

threadRecorder = ThreadRecorder.get();

ProcessProfileWriter.getProject(project.getPath())

.setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION)

.setAndroidPlugin(getAnalyticsPluginType())

.setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST);

threadRecorder.record(

ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,

project.getPath(),

null,

this::configureProject);

threadRecorder.record(

ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,

project.getPath(),

null,

this::configureExtension);

threadRecorder.record(

ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,

project.getPath(),

null,

this::createTasks);

// Apply additional plugins

for (String plugin : AndroidGradleOptions.getAdditionalPlugins(project)) {

project.apply(ImmutableMap.of("plugin", plugin));

}

}

threadRecoirder.recode()是记录最后一个参数的路径和执行的时间点,前面做了一些必要性的信息检测之前,其实主要做了以下几件事情:

// 配置项目,设置构建回调

this::configureProject

// 配置Extension

this::configureExtension

// 创建任务

this::createTasks

::是java 8引入的特性,详情可以查看java8特性 ,这里就是方法的调用

configureProject

直接来看源码

private void configureProject() {

extraModelInfo = new ExtraModelInfo(project);

checkGradleVersion();

AndroidGradleOptions.validate(project);

// Android SDK处理类

sdkHandler = new SdkHandler(project, getLogger());

// 设置项目评估阶段回调

project.afterEvaluate(p -> {

// TODO: Read flag from extension.

if (!p.getGradle().getStartParameter().isOffline()

&& AndroidGradleOptions.getUseSdkDownload(p)) {

// 相关配置依赖的下载处理

SdkLibData sdkLibData =

SdkLibData.download(getDownloader(), getSettingsController());

dependencyManager.setSdkLibData(sdkLibData);

sdkHandler.setSdkLibData(sdkLibData);

}

});

// 创建AndroidBuilder

androidBuilder = new AndroidBuilder(

project == project.getRootProject() ? project.getName() : project.getPath(),

creator,

new GradleProcessExecutor(project),

new GradleJavaProcessExecutor(project),

extraModelInfo,

getLogger(),

isVerbose());

// dataBinding的相关处理

dataBindingBuilder = new DataBindingBuilder();

dataBindingBuilder.setPrintMachineReadableOutput(

extraModelInfo.getErrorFormatMode() ==

ExtraModelInfo.ErrorFormatMode.MACHINE_PARSABLE);

// Apply the Java and Jacoco plugins.

project.getPlugins().apply(JavaBasePlugin.class);

project.getPlugins().apply(JacocoPlugin.class);

// 给assemble任务添加描述

project.getTasks()

.getByName("assemble")

.setDescription(

"Assembles all variants of all applications and secondary packages.");

...

可以看到 configureProject 方法中在 project.afterEvaluate 设置了回调,当项目评估结束时,根据项目配置情况,设置 dependece 依赖;创建了 AndroidBuilder 对象,这个对象是用来合并manifest 和创建 dex 等作用,后面在创建任务的过程中会使用到,结下来继续看 configureProject 的源码

// call back on execution. This is called after the whole build is done (not

// after the current project is done).

// This is will be called for each (android) projects though, so this should support

// being called 2+ times.

// 设置构建回调

project.getGradle()

.addBuildListener(

new BuildListener() {

private final LibraryCache libraryCache = LibraryCache.getCache();

@Override

public void buildStarted(Gradle gradle) {}

@Override

public void settingsEvaluated(Settings settings) {}

@Override

public void projectsLoaded(Gradle gradle) {}

@Override

public void projectsEvaluated(Gradle gradle) {}

@Override

public void buildFinished(BuildResult buildResult) {

ExecutorSingleton.shutdown();

sdkHandler.unload();

threadRecorder.record(

ExecutionType.BASE_PLUGIN_BUILD_FINISHED,

project.getPath(),

null,

() -> {

// 当任务执行完成时,清楚dex缓存

PreDexCache.getCache()

.clear(

FileUtils.join(

project.getRootProject()

.getBuildDir(),

FD_INTERMEDIATES,

"dex-cache",

"cache.xml"),

getLogger());

JackConversionCache.getCache()

.clear(

FileUtils.join(

project.getRootProject()

.getBuildDir(),

FD_INTERMEDIATES,

"jack-cache",

"cache.xml"),

getLogger());

libraryCache.unload();

Main.clearInternTables();

});

}

});

// 设置创建有向图任务回调

project.getGradle()

.getTaskGraph()

.addTaskExecutionGraphListener(

taskGraph -> {

for (Task task : taskGraph.getAllTasks()) {

// TransformTask是class编译成dex的重要任务

if (task instanceof TransformTask) {

Transform transform = ((TransformTask) task).getTransform();

if (transform instanceof DexTransform) {

PreDexCache.getCache()

.load(

FileUtils.join(

project.getRootProject()

.getBuildDir(),

FD_INTERMEDIATES,

"dex-cache",

"cache.xml"));

break;

} else if (transform instanceof JackPreDexTransform) {

JackConversionCache.getCache()

.load(

FileUtils.join(

project.getRootProject()

.getBuildDir(),

FD_INTERMEDIATES,

"jack-cache",

"cache.xml"));

break;

}

}

}

});

这里在添加了 BuildListener,在 buildFinished 的时候清楚了dex缓存,而在任务有向图创建的回调中,判断是否是 DexTransfrom,从而从缓存中加载dex。

总结一下 configureProject 做的事情,主要是进行版本有效性的判断,创建了 AndroidBuilder 对象,并设置了构建流程的回调来处理依赖和dex的加载和缓存清理。

configureExtension

这个阶段就是配置 extension 的阶段,就是创建我们 android 块中的可配置的对象

private void configureExtension() {

final NamedDomainObjectContainer buildTypeContainer =

project.container(

BuildType.class,

new BuildTypeFactory(instantiator, project, project.getLogger()));

final NamedDomainObjectContainer productFlavorContainer =

project.container(

ProductFlavor.class,

new ProductFlavorFactory(

instantiator, project, project.getLogger(), extraModelInfo));

final NamedDomainObjectContainer signingConfigContainer =

project.container(SigningConfig.class, new SigningConfigFactory(instantiator));

extension =

createExtension(

project,

instantiator,

androidBuilder,

sdkHandler,

buildTypeContainer,

productFlavorContainer,

signingConfigContainer,

extraModelInfo);

...

首先创建了 BuildType、ProductFlavor、SigningConfig 三个类型的Container,接着传入到了createExtension方法中,点入查看是个抽象的方法,各自的实现在子类中,这里也就是我们的AppPlugin 中

@NonNull

@Override

protected BaseExtension createExtension(

@NonNull Project project,

@NonNull Instantiator instantiator,

@NonNull AndroidBuilder androidBuilder,

@NonNull SdkHandler sdkHandler,

@NonNull NamedDomainObjectContainer buildTypeContainer,

@NonNull NamedDomainObjectContainer productFlavorContainer,

@NonNull NamedDomainObjectContainer signingConfigContainer,

@NonNull ExtraModelInfo extraModelInfo) {

return project.getExtensions()

.create(

"android",

AppExtension.class,

project,

instantiator,

androidBuilder,

sdkHandler,

buildTypeContainer,

productFlavorContainer,

signingConfigContainer,

extraModelInfo);

}

这里也就是可以看到我们android块配置是如何来的了,对应的Extension也确实是AppExtension,继续查看 configureExtension 的源码

dependencyManager = new DependencyManager(

project,

extraModelInfo,

sdkHandler);

ndkHandler = new NdkHandler(

project.getRootDir(),

null, /* compileSkdVersion, this will be set in afterEvaluate */

"gcc",

"" /*toolchainVersion*/);

taskManager =

createTaskManager(

project,

androidBuilder,

dataBindingBuilder,

extension,

sdkHandler,

ndkHandler,

dependencyManager,

registry,

threadRecorder);

variantFactory = createVariantFactory(instantiator, androidBuilder, extension);

variantManager =

new VariantManager(

project,

androidBuilder,

extension,

variantFactory,

taskManager,

instantiator,

threadRecorder);

// Register a builder for the custom tooling model

ModelBuilder modelBuilder = new ModelBuilder(

androidBuilder,

variantManager,

taskManager,

extension,

extraModelInfo,

ndkHandler,

new NativeLibraryFactoryImpl(ndkHandler),

getProjectType(),

AndroidProject.GENERATION_ORIGINAL);

registry.register(modelBuilder);

// Register a builder for the native tooling model

NativeModelBuilder nativeModelBuilder = new NativeModelBuilder(variantManager);

registry.register(nativeModelBuilder);

这一部分主要是创建一些管理类,其中 createTaskManager、createVariantFactory 都是抽象方法,对应的实现类

createTaskManager

AppPlugin -> ApplicationTaskManager

LibraryPlugin -> LibraryTaskManager

createVariantFactory

AppPlugin -> ApplicationVariantFactory

LibraryPlugin -> LibraryVariantFactory

这里简单介绍一下 TaskManager 就是创建具体任务的管理类,app 工程和库 library 工程所需的构建任务是不同的,后面我们会介绍 app 工程创建的构建任务;VariantFactory 就是我们常说的构建变体的工厂类,主要是生成Variant(构建变体)的对象。我们回到 createExtension 的源码中

// map the whenObjectAdded callbacks on the containers.

signingConfigContainer.whenObjectAdded(variantManager::addSigningConfig);

buildTypeContainer.whenObjectAdded(

buildType -> {

SigningConfig signingConfig =

signingConfigContainer.findByName(BuilderConstants.DEBUG);

buildType.init(signingConfig);

variantManager.addBuildType(buildType);

});

productFlavorContainer.whenObjectAdded(variantManager::addProductFlavor);

...

// create default Objects, signingConfig first as its used by the BuildTypes.

variantFactory.createDefaultComponents(

buildTypeContainer, productFlavorContainer, signingConfigContainer);

这一部分做得事情,配置了 BuildTypeContainer、ProductFlavorContainer、SigningConfigContainer 这三个配置项的 whenObjectAdded 的回调,每个配置的添加都会加入到 variantManager 中;创建默认配置,下面是 ApplicationVariantFactory 的 createDefaultComponents 代码

@Override

public void createDefaultComponents(

@NonNull NamedDomainObjectContainer buildTypes,

@NonNull NamedDomainObjectContainer productFlavors,

@NonNull NamedDomainObjectContainer signingConfigs) {

// must create signing config first so that build type 'debug' can be initialized

// with the debug signing config.

signingConfigs.create(DEBUG);

buildTypes.create(DEBUG);

buildTypes.create(RELEASE);

}

总结一下 configureExtension 方法的作用,主要是创建 Android 插件的扩展对象,对配置项 BuildType、ProductFlavor、SigningConfig 做了统一的创建和回调处理, 创建taskManager、variantFactory、variantManager。

createTasks

private void createTasks() {

threadRecorder.record(

ExecutionType.TASK_MANAGER_CREATE_TASKS,

project.getPath(),

null,

() -> // 在项目评估之前创建任务

taskManager.createTasksBeforeEvaluate(

new TaskContainerAdaptor(project.getTasks())));

project.afterEvaluate(

project ->

threadRecorder.record(

ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,

project.getPath(),

null,

// 在项目评估完成之后创建 androidTask

() -> createAndroidTasks(false)));

}

这里主要是分两块,一个是在 beforeEvaluate 创建任务;一个是在 afterEvaluate 创建任务。这里的区别是 AndroidTask 是依赖配置项的配置才能生成相应任务,所以是需要在 afterEvaluate 之后创建,如果对项目评估回调不理解的话,可以查阅Project文档。beforeEvaluate 创建的任务跟我们编译没有太大关系,我们重点查看一下 afterEvaluate 创建的任务 createAndroidTasks

@VisibleForTesting

final void createAndroidTasks(boolean force) {

...

threadRecorder.record(

ExecutionType.VARIANT_MANAGER_CREATE_ANDROID_TASKS,

project.getPath(),

null,

() -> {

// 创建AndroidTasks

variantManager.createAndroidTasks();

ApiObjectFactory apiObjectFactory =

new ApiObjectFactory(

androidBuilder, extension, variantFactory, instantiator);

for (BaseVariantData variantData : variantManager.getVariantDataList()) {

apiObjectFactory.create(variantData);

}

});

...

}

我们主要看下variantManager的createAndroidTasks的方法

/**

* Variant/Task creation entry point.

*

* Not used by gradle-experimental.

*/

public void createAndroidTasks() {

variantFactory.validateModel(this);

variantFactory.preVariantWork(project);

final TaskFactory tasks = new TaskContainerAdaptor(project.getTasks());

if (variantDataList.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(tasks, !productFlavors.isEmpty()));

for (final BaseVariantData extends BaseVariantOutputData> variantData : variantDataList) {

recorder.record(

ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT,

project.getPath(),

variantData.getName(),

() -> createTasksForVariantData(tasks, variantData));

}

taskManager.createReportTasks(tasks, variantDataList);

}

首先判断 variantDataList 是否是空,如果是空的就会进入到 populateVariantDataList 方法中

/**

* Create all variants.

*/

public void populateVariantDataList() {

if (productFlavors.isEmpty()) {

createVariantDataForProductFlavors(Collections.emptyList());

} else {

List flavorDimensionList = extension.getFlavorDimensionList();

// Create iterable to get GradleProductFlavor from ProductFlavorData.

Iterable flavorDsl =

Iterables.transform(

productFlavors.values(),

ProductFlavorData::getProductFlavor);

// Get a list of all combinations of product flavors.

List> flavorComboList =

ProductFlavorCombo.createCombinations(

flavorDimensionList,

flavorDsl);

for (ProductFlavorCombo flavorCombo : flavorComboList) {

//noinspection unchecked

createVariantDataForProductFlavors(

(List) (List) flavorCombo.getFlavorList());

}

}

}

从方法注释可以看到,这个方法主要的作用就是创建所有的 variants,试想一下该段代码会做哪些事情,是否是解析 buildType、productFlavor 配置?

创建构建变体(BuildVariant)

继续观察上面的代码,可以看到无论是否有配置productFlavor 子项,都会进入到 createVariantDataForProductFlavors 方法。如果有配置的话,通过获取配置的 flavorDimension 和 productFlavor 数组,调用 ProductFlavorCombo.createCombinations 组合出最后的产品风味数组 flavorComboList ,最后通过遍历调用 createVariantDataForProductFlavors 方法

/**

* Creates VariantData for a specified list of product flavor.

*

* This will create VariantData for all build types of the given flavors.

*

* @param productFlavorList the flavor(s) to build.

*/

private void createVariantDataForProductFlavors(

@NonNull List productFlavorList) {

...

for (BuildTypeData buildTypeData : buildTypes.values()) {

boolean ignore = false;

...

if (!ignore) {

BaseVariantData> variantData = createVariantData(

buildTypeData.getBuildType(),

productFlavorList);

variantDataList.add(variantData);

...

}

}

...

}

看上述代码,通过 creatVariantData 方法,将 buildType 和 productFlavor 的作为参数传入,创建了 variantData,并且加入到了 variantDataList 集合中,这里我们就是将所有的构建变体集合到了 variantDataList 中。

接着我们返回继续看 createAndroidTasks 方法

/**

* Variant/Task creation entry point.

*

* Not used by gradle-experimental.

*/

public void createAndroidTasks() {

...

for (final BaseVariantData extends BaseVariantOutputData> variantData : variantDataList) {

recorder.record(

ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT,

project.getPath(),

variantData.getName(),

() -> createTasksForVariantData(tasks, variantData));

}

...

}

通过上面拿到的variantDataList,遍历该集合来创建任务

/**

* Create tasks for the specified variantData.

*/

public void createTasksForVariantData(

final TaskFactory tasks,

final BaseVariantData extends BaseVariantOutputData> variantData) {

final BuildTypeData buildTypeData = buildTypes.get(

variantData.getVariantConfiguration().getBuildType().getName());

if (buildTypeData.getAssembleTask() == null) {

// 创建assemble + buildType任务

buildTypeData.setAssembleTask(taskManager.createAssembleTask(tasks, buildTypeData));

}

// Add dependency of assemble task on assemble build type task.

tasks.named("assemble", new Action() {

@Override

public void execute(Task task) {

assert buildTypeData.getAssembleTask() != null;

// 将 assemble 任务依赖于我们的 assemble + buildType 任务

task.dependsOn(buildTypeData.getAssembleTask().getName());

}

});

VariantType variantType = variantData.getType();

// 根据 variantData 创建 assemble + flavor + buildType 任务

createAssembleTaskForVariantData(tasks, variantData);

if (variantType.isForTesting()) {

...

} else {

// 根据 variantData 创建一系列任务

taskManager.createTasksForVariantData(tasks, variantData);

}

}

首先会先根据 buildType 信息创建 assemble + buildType 的任务,可以看下taskManager. createAssembleTask里的代码

@NonNull

public AndroidTask createAssembleTask(

@NonNull TaskFactory tasks,

@NonNull VariantDimensionData dimensionData) {

final String sourceSetName =

StringHelper.capitalize(dimensionData.getSourceSet().getName());

return androidTasks.create(

tasks,

// 设置任务名字为 assembleXXX

"assemble" + sourceSetName,

assembleTask -> {

// 设置描述和任务组

assembleTask.setDescription("Assembles all " + sourceSetName + " builds.");

assembleTask.setGroup(BasePlugin.BUILD_GROUP);

});

}

创建完任务之后,将assemble任务依赖于我们的assembleXXX任务,随后调用 createAssembleTaskForVariantData 方法,此方法是创建 assemble + flavor + buildType 任务,流程多了 productFlavor 任务的创建,这里就不赘述了。后面会执 createTasksForVariantData,这个方法就是根据 variant 生成一系列 Android 构建所需任务(后面会详细介绍),回到 createAndroidTasks 方法中

threadRecorder.record(

ExecutionType.VARIANT_MANAGER_CREATE_ANDROID_TASKS,

project.getPath(),

null,

() -> {

variantManager.createAndroidTasks();

ApiObjectFactory apiObjectFactory =

new ApiObjectFactory(

androidBuilder, extension, variantFactory, instantiator);

for (BaseVariantData variantData : variantManager.getVariantDataList()) {

// 创建variantApi,添加到extensions中

apiObjectFactory.create(variantData);

}

});

最后就遍历 variantDataList 通过 ApiObjectFactory 创建 variantApi,添加到 extensions 中;

至此,我们就已经将配置的构建变种任务已经添加到我们的任务列表中,并形成了相关依赖。

Application 的编译任务

我们继续查看createTasksForVariantData的最后一行,

taskManager.createTasksForVariantData,发现 createTasksForVariantData 是抽象方法,这里的 taskManager 具体实现是 ApplicationTaskManager,查看 ApplicationTaskManager 的 createTasksForVariantData 方法

/**

* Creates the tasks for a given BaseVariantData.

*/

@Override

public void createTasksForVariantData(

@NonNull final TaskFactory tasks,

@NonNull final BaseVariantData extends BaseVariantOutputData> variantData) {

assert variantData instanceof ApplicationVariantData;

final VariantScope variantScope = variantData.getScope();

//create sourceGenTask, resGenTask, assetGenTask

createAnchorTasks(tasks, variantScope);

createCheckManifestTask(tasks, variantScope);

handleMicroApp(tasks, variantScope);

// Create all current streams (dependencies mostly at this point)

createDependencyStreams(tasks, variantScope);

// Add a task to process the manifest(s)

// Add a task to create the res values

// Add a task to compile renderscript files.

// Add a task to merge the resource folders

// Add a task to merge the asset folders

// Add a task to create the BuildConfig class

// Add a task to process the Android Resources and generate source files

// Add a task to process the java resources

// Add a task to process this aidl file

// Add a task to process shader source

// Add NDK tasks

// Add external native build tasks

// Add a task to merge the jni libs folders

// Add a compile task

// Add data binding tasks if enabled

// create packaging task

// create the lint tasks.

...

}

代码实在太长了,我只留下了每段代码的注释,注释也已经非常清楚了,这个主要就是生成 variantData 的一系列像 compileXXX、generateXXX、processXXX、mergeXXX的任务,这一系列 task 就是构建一个可运行的完整APK的所需的所有task。下面介绍在编译dex中的过程,涉及的几个task。

Dex的编译过程

// Add a compile task

recorder.record(

ExecutionType.APP_TASK_MANAGER_CREATE_COMPILE_TASK,

project.getPath(),

variantScope.getFullVariantName(),

() -> {

CoreJackOptions jackOptions =

variantData.getVariantConfiguration().getJackOptions();

// create data binding merge task before the javac task so that it can

// parse jars before any consumer

createDataBindingMergeArtifactsTaskIfNecessary(tasks, variantScope);

AndroidTask extends JavaCompile> javacTask =

// 创建 javac 任务

createJavacTask(tasks, variantScope);

if (jackOptions.isEnabled()) {

AndroidTask jackTask =

createJackTask(tasks, variantScope, true /*compileJavaSource*/);

setJavaCompilerTask(jackTask, tasks, variantScope);

} else {

...

addJavacClassesStream(variantScope);

setJavaCompilerTask(javacTask, tasks, variantScope);

getAndroidTasks()

.create(

tasks,

// 创建 AndroidJarTask ,生成classes.jar

new AndroidJarTask.JarClassesConfigAction(variantScope));

createPostCompilationTasks(tasks, variantScope);

}

});

我们直接查看 Add a compile task 注释下的代码,在执行 createPostCompilationTasks 之前,先创建了 javac 任务,任务名称为 compileXXXJavaWithJavac ,该任务是将 java 源文件编译成 class 文件,具体实现是在 JavaCompileConfigAction 类中。创建 javac 任务之后,接着创建了 AndroidJarTask 任务,该任务是将 class 文件整合输出 jar 包,具体实现就是在 AndroidJarTask 类中。

紧接着我们来看一下 createPostCompilationTasks 的方法

/**

* Creates the post-compilation tasks for the given Variant.

*

* These tasks create the dex file from the .class files, plus optional intermediary steps like

* proguard and jacoco

*

*/

public void createPostCompilationTasks(

@NonNull TaskFactory tasks,

@NonNull final VariantScope variantScope) {

checkNotNull(variantScope.getJavacTask());

variantScope.getInstantRunBuildContext().setInstantRunMode(

getIncrementalMode(variantScope.getVariantConfiguration()) != IncrementalMode.NONE);

final BaseVariantData extends BaseVariantOutputData> variantData = variantScope.getVariantData();

final GradleVariantConfiguration config = variantData.getVariantConfiguration();

TransformManager transformManager = variantScope.getTransformManager();

...

boolean isMinifyEnabled = isMinifyEnabled(variantScope);

boolean isMultiDexEnabled = config.isMultiDexEnabled();

// Switch to native multidex if possible when using instant run.

boolean isLegacyMultiDexMode = isLegacyMultidexMode(variantScope);

AndroidConfig extension = variantScope.getGlobalScope().getExtension();

// ----- External Transforms -----

// apply all the external transforms.

...

// ----- Minify next -----

if (isMinifyEnabled) {

boolean outputToJarFile = isMultiDexEnabled && isLegacyMultiDexMode;

// 内部会判断是否使用 proguard 来创建 proguard 任务和 shrinkResources 任务

createMinifyTransform(tasks, variantScope, outputToJarFile);

}

// ----- 10x support

...

// ----- Multi-Dex support

Optional> multiDexClassListTask;

// non Library test are running as native multi-dex

if (isMultiDexEnabled && isLegacyMultiDexMode) {

...

} else {

multiDexClassListTask = Optional.empty();

}

// create dex transform

// 从 extension 中获取 dexOptions 项的配置

DefaultDexOptions dexOptions = DefaultDexOptions.copyOf(extension.getDexOptions());

...

// 创建 DexTransform

DexTransform dexTransform = new DexTransform(

dexOptions,

config.getBuildType().isDebuggable(),

isMultiDexEnabled,

isMultiDexEnabled && isLegacyMultiDexMode ? variantScope.getMainDexListFile() : null,

variantScope.getPreDexOutputDir(),

variantScope.getGlobalScope().getAndroidBuilder(),

getLogger(),

variantScope.getInstantRunBuildContext(),

AndroidGradleOptions.getBuildCache(variantScope.getGlobalScope().getProject()));

// 创建 dexTask

Optional> dexTask =

transformManager.addTransform(tasks, variantScope, dexTransform);

// need to manually make dex task depend on MultiDexTransform since there's no stream

// consumption making this automatic

dexTask.ifPresent(t -> {

t.optionalDependsOn(tasks, multiDexClassListTask.orElse(null));

variantScope.addColdSwapBuildTask(t);

});

...

}

为了讲述主流程,我将一些 mutiDex 和 instantRun 判断的源码省略了,这里我们关注非mutiDex和非instantRun的情况。我们看到,如果我们设置了 minifyEnabled 为 true,那么这里就会去创建 createMinifyTransform ,如果use proguard,这里会创建 progruad 的任务和 shrinkResources 的任务。后面将创建 dexTask, 这个是 transfromTask 类型的任务,我们先来看下 transFromTask 类

/**

* A task running a transform.

*/

@ParallelizableTask

public class TransformTask extends StreamBasedTask implements Context {

private Transform transform;

...

public Transform getTransform() {

return transform;

}

...

@TaskAction

void transform(final IncrementalTaskInputs incrementalTaskInputs)

throws IOException, TransformException, InterruptedException {

...

recorder.record(

ExecutionType.TASK_TRANSFORM,

executionInfo,

getProject().getPath(),

getVariantName(),

new Recorder.Block() {

@Override

public Void call() throws Exception {

transform.transform(

new TransformInvocationBuilder(TransformTask.this)

.addInputs(consumedInputs.getValue())

.addReferencedInputs(referencedInputs.getValue())

.addSecondaryInputs(changedSecondaryInputs.getValue())

.addOutputProvider(

outputStream != null

? outputStream.asOutput()

: null)

.setIncrementalMode(isIncremental.getValue())

.build());

return null;

}

});

}

}

我们知道,自定义任务中,在任务执行阶段会去执行被 @TaskAction 注解的方法,这里也就是执行 transfrom 方法,而 transfrom 方法中最后又会调用到 transform 的 transfrom 方法,在我们 dexTask 中传入的 transfrom 是DexTransfrom,那我们就去看下 DexTransfrom 的 transfrom 具体实现

public class DexTransform extends Transform {

@Override

public void transform(@NonNull TransformInvocation transformInvocation)

throws TransformException, IOException, InterruptedException {

...

try {

// if only one scope or no per-scope dexing, just do a single pass that

// runs dx on everything.

if ((jarInputs.size() + directoryInputs.size()) == 1

|| !dexOptions.getPreDexLibraries()) {

// since there is only one dex file, we can merge all the scopes into the full

// application one.

File outputDir = outputProvider.getContentLocation("main",

getOutputTypes(),

TransformManager.SCOPE_FULL_PROJECT,

Format.DIRECTORY);

FileUtils.mkdirs(outputDir);

// first delete the output folder where the final dex file(s) will be.

FileUtils.cleanOutputDir(outputDir);

// gather the inputs. This mode is always non incremental, so just

// gather the top level folders/jars

final List inputFiles =

Stream.concat(

jarInputs.stream().map(JarInput::getFile),

directoryInputs.stream().map(DirectoryInput::getFile))

.collect(Collectors.toList());

// 通过 AndroidBuilder 转化为 byte

androidBuilder.convertByteCode(

inputFiles,

outputDir,

multiDex,

mainDexListFile,

dexOptions,

outputHandler);

for (File file : Files.fileTreeTraverser().breadthFirstTraversal(outputDir)) {

if (file.isFile()) {

instantRunBuildContext.addChangedFile(FileType.DEX, file);

}

}

} else {

...

}

最后执行到androidBuilder.convertByteCode

/**

* Converts the bytecode to Dalvik format

* @param inputs the input files

* @param outDexFolder the location of the output folder

* @param dexOptions dex options

* @throws IOException

* @throws InterruptedException

* @throws ProcessException

*/

public void convertByteCode(

@NonNull Collection inputs,

@NonNull File outDexFolder,

boolean multidex,

@Nullable File mainDexList,

@NonNull DexOptions dexOptions,

@NonNull ProcessOutputHandler processOutputHandler)

throws IOException, InterruptedException, ProcessException {checkNotNull(inputs, "inputs cannot be null.");

checkNotNull(outDexFolder, "outDexFolder cannot be null.");

checkNotNull(dexOptions, "dexOptions cannot be null.");

checkArgument(outDexFolder.isDirectory(), "outDexFolder must be a folder");

checkState(mTargetInfo != null,

"Cannot call convertByteCode() before setTargetInfo() is called.");

ImmutableList.Builder verifiedInputs = ImmutableList.builder();

for (File input : inputs) {

if (checkLibraryClassesJar(input)) {

verifiedInputs.add(input);

}

}

//创建 DexProcessBuilder

DexProcessBuilder builder = new DexProcessBuilder(outDexFolder);

builder.setVerbose(mVerboseExec)

.setMultiDex(multidex)

.setMainDexList(mainDexList)

.addInputs(verifiedInputs.build());

runDexer(builder, dexOptions, processOutputHandler);

}

创建了 DexProcessBuilder ,随后执行到了 runDexer 方法中

public void runDexer(

@NonNull final DexProcessBuilder builder,

@NonNull final DexOptions dexOptions,

@NonNull final ProcessOutputHandler processOutputHandler)

throws ProcessException, IOException, InterruptedException {

initDexExecutorService(dexOptions);

if (dexOptions.getAdditionalParameters().contains("--no-optimize")) {

mLogger.warning(DefaultDexOptions.OPTIMIZE_WARNING);

}

if (shouldDexInProcess(dexOptions)) {

dexInProcess(builder, dexOptions, processOutputHandler);

} else {

dexOutOfProcess(builder, dexOptions, processOutputHandler);

}

}

进入到dexInProcess方法

private void dexInProcess(

@NonNull final DexProcessBuilder builder,

@NonNull final DexOptions dexOptions,

@NonNull final ProcessOutputHandler outputHandler)

throws IOException, ProcessException {

final String submission = Joiner.on(',').join(builder.getInputs());

mLogger.verbose("Dexing in-process : %1$s", submission);

try {

sDexExecutorService.submit(() -> {

Stopwatch stopwatch = Stopwatch.createStarted();

ProcessResult result = DexWrapper.run(builder, dexOptions, outputHandler);

result.assertNormalExitValue();

mLogger.verbose("Dexing %1$s took %2$s.", submission, stopwatch.toString());

return null;

}).get();

} catch (Exception e) {

throw new ProcessException(e);

}

}

/**

* Wrapper around the real dx classes.

*/

public class DexWrapper {

/**

* Runs the dex command.

*

* @return the integer return code of com.android.dx.command.dexer.Main.run()

*/

public static ProcessResult run(

@NonNull DexProcessBuilder processBuilder,

@NonNull DexOptions dexOptions,

@NonNull ProcessOutputHandler outputHandler) throws IOException, ProcessException {

ProcessOutput output = outputHandler.createOutput();

int res;

try {

DxContext dxContext = new DxContext(output.getStandardOutput(), output.getErrorOutput());

// 构建 Main.Arguments 参数

Main.Arguments args = buildArguments(processBuilder, dexOptions, dxContext);

res = new Main(dxContext).run(args);

} finally {

output.close();

}

outputHandler.handleOutput(output);

return new DexProcessResult(res);

}

...

}

buildArguments方法通过传入的DexProcessBuilder、dexOptions、dxContext构建 arguments,后面使用的args的参数fileNames,outName,jarOutput都是从DexProcessBuilder来的,然后执行Main的run方法

package com.android.dx.command.dexer;

...

/**

* Main class for the class file translator.

*/

public class Main {

/**

* Run and return a result code.

* @param arguments the data + parameters for the conversion

* @return 0 if success > 0 otherwise.

*/

public int run(Arguments arguments) throws IOException {

// Reset the error count to start fresh.

errors.set(0);

// empty the list, so that tools that load dx and keep it around

// for multiple runs don't reuse older buffers.

libraryDexBuffers.clear();

args = arguments;

args.makeOptionsObjects(context);

OutputStream humanOutRaw = null;

if (args.humanOutName != null) {

humanOutRaw = openOutput(args.humanOutName);

humanOutWriter = new OutputStreamWriter(humanOutRaw);

}

try {

if (args.multiDex) {

return runMultiDex();

} else {

return runMonoDex();

}

} finally {

closeOutput(humanOutRaw);

}

}

}

这里我们关注非multiDex的情况,即执行了runMonoDex的方法

private int runMonoDex() throws IOException {

...

// 内部会创建dexFile,并填充class

if (!processAllFiles()) {

return 1;

}

if (args.incremental && !anyFilesProcessed) {

return 0; // this was a no-op incremental build

}

// this array is null if no classes were defined

byte[] outArray = null;

if (!outputDex.isEmpty() || (args.humanOutName != null)) {

// 内部通过Dex类toDex 方法将 class 文件转化dex byte[]

outArray = writeDex(outputDex);

if (outArray == null) {

return 2;

}

}

if (args.incremental) {

outArray = mergeIncremental(outArray, incrementalOutFile);

}

outArray = mergeLibraryDexBuffers(outArray);

if (args.jarOutput) {

// Effectively free up the (often massive) DexFile memory.

outputDex = null;

if (outArray != null) {

// 输出的文件名为 classes.dex

outputResources.put(DexFormat.DEX_IN_JAR_NAME, outArray);

}

if (!createJar(args.outName)) {

return 3;

}

} else if (outArray != null && args.outName != null) {

OutputStream out = openOutput(args.outName);

out.write(outArray);

closeOutput(out);

}

return 0;

}

上面的代码中,填充class以及dex流转换,内部流程较为复杂,就不再继续深入,简单做下总结:

1.通过执行 processAllFiles ,内部创建 DexFile 也就是outputDex,并且填充 class 文件

2.通过 writeDex 方法,将 outputDex 传入,方法内部执行的是 outputDex.toDex 方法,将 outputDex 内部填充的 class 转化为 dex 的 byte[] 返回

3.最后将 byte[] 数组创建 classes.dex 输出

总结:

Android Gradle Plugin源码繁多,以上文章只是对整体流程的简单梳理,其中简要介绍了构建变体任务的解析和添加 ,最后对编译dex流程做了简单分析。个人精力有限,这里的源码解析也只是九牛一毛,如有纰漏,欢迎大家拍砖,希望这篇文章能帮助到想了解 Android Gradle Plguin 原理的同学。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值