如今Android开发最经常使用的IDE就是Android Studio了。在Android Studio中使用了Gradle构建功能,这使得模块之间的管理、依赖都很是的方便清晰。java
同时,国内比较火热的Android插件化、热更新等都涉及到了Gradle插件的知识,熟练的掌握Gradle,可让咱们更加清楚的了解Android的构建过程,改造构建过程以达到某些功能需求。从这个角度来讲,Android开发是须要掌握Gradle这项技能的。android
在Android项目的build.gradle中,有这样的一行代码:缓存
apply plugin: 'com.android.application'
复制代码
想必你们对这行代码都至关的熟悉吧。它要表达的意思是将名为com.android.application
的插件运用到咱们的项目中,这个插件就是大名鼎鼎的Android Plugin。bash
那么Android Plugin是怎么进入到咱们的工程中的呢? app
咱们将上面的gradle插件复制到项目的build.gradle中(注意:这里是项目的build.gradle,和上面的build.gradle不同!)。 ide
同步(sync)一下项目,就能够在项目的依赖树中找到Android Plugin源码。 gradle
展开上面的com.android.tools.build:gradle:3.1.0@jar
,能够看到AppPlugin和LibraryPlugin,其中AppPlugin就是Android项目须要依赖的插件,而LibraryPlugin是组件项目须要依赖的插件。 ui
下面咱们就解析一下AppPlugin的源码,并从中了解App构建过程。Let's do it~this
首先AppPlugin继承了BasePlugin,BasePlugin实现了Plugin接口,并实现了其中的apply方法。apply方法做为BasePlugin的入口类,其实现以下:lua
@Override
public void apply(@NonNull Project project) {
//初始化项,省略
//构造线程记录器,用于记录执行时间
threadRecorder = ThreadRecorder.get();
ProcessProfileWriter.getProject(project.getPath())
.setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION)
.setAndroidPlugin(getAnalyticsPluginType())
.setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST)
.setOptions(AnalyticsUtil.toProto(projectOptions));
BuildableArtifactImpl.Companion.disableResolution();
if (!projectOptions.get(BooleanOption.ENABLE_NEW_DSL_AND_API)) {
//不使用新的DSL API
TaskInputHelper.enableBypass();
// configureProject 配置项目
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
project.getPath(),
null,
this::configureProject);
// configureExtension 配置 Extension,以后咱们才能使用 android {} 进行配置
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
project.getPath(),
null,
this::configureExtension);
// createTasks 建立必须的 task
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
project.getPath(),
null,
this::createTasks);
} else {
//省略
}
}
复制代码
上面的apply方法最重要的是调用了三次ThreadRecorder的record方法。注意:this::configureProject是java8 的lambda写法,因此接下来咱们看一下record方法的实现:
@Override
public void record(
@NonNull ExecutionType executionType,
@NonNull String projectPath,
@Nullable String variant,
@NonNull VoidBlock block) {
ProfileRecordWriter profileRecordWriter = ProcessProfileWriter.get();
//建立GradleBuildProfileSpan.Builder对象
GradleBuildProfileSpan.Builder currentRecord =
create(profileRecordWriter, executionType, null);
try {
//回调方法
block.call();
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
//将上面建立的GradleBuildProfileSpan对象写入到ProfileRecordWriter对象的span(队列)变量中
write(profileRecordWriter, currentRecord, projectPath, variant);
}
}
复制代码
record方法主要完成了三个逻辑: 一、建立了建立GradleBuildProfileSpan.Builder对象; 二、执行回调方法; 三、最后将GradleBuildProfileSpan对象写入到ProfileRecordWriter对象的span(队列)变量中。
首先咱们看1的逻辑,调用了create方法建立了GradleBuildProfileSpan.Builder对象:
private GradleBuildProfileSpan.Builder create(
@NonNull ProfileRecordWriter profileRecordWriter,
@NonNull ExecutionType executionType,
@Nullable GradleTransformExecution transform) {
long thisRecordId = profileRecordWriter.allocateRecordId();
// am I a child ?
@Nullable
Long parentId = recordStacks.get().peek();
long startTimeInMs = System.currentTimeMillis();
final GradleBuildProfileSpan.Builder currentRecord =
GradleBuildProfileSpan.newBuilder()
.setId(thisRecordId)
.setType(executionType)
.setStartTimeInMs(startTimeInMs);
if (transform != null) {
currentRecord.setTransform(transform);
}
if (parentId != null) {
currentRecord.setParentId(parentId);
}
currentRecord.setThreadId(threadId.get());
recordStacks.get().push(thisRecordId);
return currentRecord;
}
复制代码
create方法的逻辑就是建立了一个GradleBuildProfileSpan.Builder对象,而且将一些线程相关的变量设置进去,并将thisRecordId保存到一个双向队列中。
而后再看3的逻辑:
private void write(
@NonNull ProfileRecordWriter profileRecordWriter,
@NonNull GradleBuildProfileSpan.Builder currentRecord,
@NonNull String projectPath,
@Nullable String variant) {
// pop this record from the stack.
if (recordStacks.get().pop() != currentRecord.getId()) {
Logger.getLogger(ThreadRecorder.class.getName())
.log(Level.SEVERE, "Profiler stack corrupted");
}
currentRecord.setDurationInMs(
System.currentTimeMillis() - currentRecord.getStartTimeInMs());
profileRecordWriter.writeRecord(projectPath, variant, currentRecord);
}
复制代码
其中执行了ProfileRecordWriter对象的writeRecord方法:
@Override
public void writeRecord(
@NonNull String project,
@Nullable String variant,
@NonNull final GradleBuildProfileSpan.Builder executionRecord) {
executionRecord.setProject(mNameAnonymizer.anonymizeProjectPath(project));
executionRecord.setVariant(mNameAnonymizer.anonymizeVariant(project, variant));
spans.add(executionRecord.build());
}
复制代码
最后运用建造者模式生成GradleBuildProfileSpan对象写入到ProfileRecordWriter对象的span(队列)变量中。
最后咱们看2的逻辑,即回调方法的执行。还记得上面提到过的BasePlugin的apply方法执行的三个record方法吗?其中每个record方法都有本身的回调方法,即configureProject、configureExtension、createTasks。
其实从这三个方法传进来的第一个参数,咱们能大概看出每个方法实现的逻辑: 一、BASE_PLUGIN_PROJECT_CONFIGURE:插件的基本配置信息、初始化等。 二、BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION:插件Extension的初始化。 三、BASE_PLUGIN_PROJECT_TASKS_CREATION:插件任务的建立。
咱们先来看第一个第一个方法的实现逻辑,后面两个方法放到后面的两篇文章来说。
private void configureProject() {
final Gradle gradle = project.getGradle();
extraModelInfo = new ExtraModelInfo(project.getPath(), projectOptions, project.getLogger());
checkGradleVersion(project, getLogger(), projectOptions);
sdkHandler = new SdkHandler(project, getLogger());
if (!gradle.getStartParameter().isOffline()
&& projectOptions.get(BooleanOption.ENABLE_SDK_DOWNLOAD)) {
SdkLibData sdkLibData = SdkLibData.download(getDownloader(), getSettingsController());
sdkHandler.setSdkLibData(sdkLibData);
}
androidBuilder =
new AndroidBuilder(
project == project.getRootProject() ? project.getName() : project.getPath(),
creator,
new GradleProcessExecutor(project),
new GradleJavaProcessExecutor(project),
extraModelInfo.getSyncIssueHandler(),
extraModelInfo.getMessageReceiver(),
getLogger(),
isVerbose());
dataBindingBuilder = new DataBindingBuilder();
dataBindingBuilder.setPrintMachineReadableOutput(
SyncOptions.getErrorFormatMode(projectOptions) == ErrorFormatMode.MACHINE_PARSABLE);
if (projectOptions.hasRemovedOptions()) {
androidBuilder
.getIssueReporter()
.reportWarning(Type.GENERIC, projectOptions.getRemovedOptionsErrorMessage());
}
if (projectOptions.hasDeprecatedOptions()) {
extraModelInfo
.getDeprecationReporter()
.reportDeprecatedOptions(projectOptions.getDeprecatedOptions());
}
// Apply the Java plugin
project.getPlugins().apply(JavaBasePlugin.class);
project.getTasks()
.getByName("assemble")
.setDescription(
"Assembles all variants of all applications and secondary packages.");
// 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.
gradle.addBuildListener(
new BuildListener() {
@Override
public void buildStarted(@NonNull Gradle gradle) {
TaskInputHelper.enableBypass();
BuildableArtifactImpl.Companion.disableResolution();
}
@Override
public void settingsEvaluated(@NonNull Settings settings) {}
@Override
public void projectsLoaded(@NonNull Gradle gradle) {}
@Override
public void projectsEvaluated(@NonNull Gradle gradle) {}
@Override
public void buildFinished(@NonNull BuildResult buildResult) {
// Do not run buildFinished for included project in composite build.
if (buildResult.getGradle().getParent() != null) {
return;
}
sdkHandler.unload();
threadRecorder.record(
ExecutionType.BASE_PLUGIN_BUILD_FINISHED,
project.getPath(),
null,
() -> {
WorkerActionServiceRegistry.INSTANCE
.shutdownAllRegisteredServices(
ForkJoinPool.commonPool());
PreDexCache.getCache()
.clear(
FileUtils.join(
project.getRootProject().getBuildDir(),
FD_INTERMEDIATES,
"dex-cache",
"cache.xml"),
getLogger());
Main.clearInternTables();
});
}
});
gradle.getTaskGraph()
.addTaskExecutionGraphListener(
taskGraph -> {
TaskInputHelper.disableBypass();
Aapt2DaemonManagerService.registerAaptService(
Objects.requireNonNull(androidBuilder.getTargetInfo())
.getBuildTools(),
loggerWrapper,
WorkerActionServiceRegistry.INSTANCE);
for (Task task : taskGraph.getAllTasks()) {
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;
}
}
}
});
createLintClasspathConfiguration(project);
}
复制代码
configureProject这个方法代码有点长,可是其主要执行了如下几个逻辑: 一、初始化了SdkHandler、AndroidBuilder和DataBindingBuilder对象。 二、依赖了JavaBasePlugin插件,在其中建立了不少关于构建的task,其中build、assemble等task就是在里面建立的。 三、对项目的建立作了监听,在构建结束后执行了磁盘缓存等操做。
本文主要介绍了如何查看Android Plugin插件源码,以及对Android Plugin插件的执行流程进行了简单的分析,后面会继续接着分析插件Extension和插件任务的建立这两个流程。