为了可以查看 Android Gradle Plugin 与 Gradle 的源码,咱们须要在项目中添加 android gradle plugin 依赖,以下所示:html
compile 'com.android.tools.build:gradle:3.0.1'
复制代码
众所周知,咱们可以将一个 moudle 构建为一个 Android 项目是因为在 build.gradle 中配置了以下的插件应用代码:java
apply plugin: 'com.android.application'
复制代码
当执行到 apply plugin: 'com.android.application' 这行配置时,也就开始了 AppPlugin 的构建流程,下面咱们就来分析下 AppPlugin 的构建流程。android
'com.android.application' 对应的插件 properties 为 'com.android.internal.application',内部标明的插件实现类以下所示:git
implementation-class=com.android.build.gradle.internal.plugins.AppPlugin
复制代码
AppPlugin 中的关键实现代码以下所示:github
/** Gradle plugin class for 'application' projects, applied on the base application module */
public class AppPlugin extends AbstractAppPlugin {
...
// 应用指定的 plugin,这里是一个空实现
@Override
protected void pluginSpecificApply(@NonNull Project project) {
}
...
// 获取一个扩展类:应用 application plugin 都会提供一个与之对应的 android extension
@Override
@NonNull
protected Class<? extends AppExtension> getExtensionClass() {
return BaseAppModuleExtension.class;
}
...
}
复制代码
那 apply 方法是在什么地方被调用的呢?web
首先,咱们梳理下 AppPlugin 的继承关系,以下所示:json
AppPlugin => AbstractAppPlugin => BasePlugin
复制代码
而 apply 方法就在 BasePlugin 类中,BasePlugin 是一个应用于全部 Android Plugin 的基类,在 apply 方法中会预先进行一些准备工做。缓存
当编译器执行到 apply plugin 这行 groovy 代码时,gradle 便会最终回调 BasePlugin 基类 的 apply 方法,以下所示:微信
@Override
public final void apply(@NonNull Project project) {
CrashReporting.runAction(
() -> {
basePluginApply(project);
pluginSpecificApply(project);
});
}
复制代码
在 apply 方法中调用了 basePluginApply 方法,其源码以下所示:闭包
private void basePluginApply(@NonNull Project project) {
...
// 一、DependencyResolutionChecks 会检查并确保在配置阶段不去解析依赖。
DependencyResolutionChecks.registerDependencyCheck(project, projectOptions);
// 二、应用一个 AndroidBasePlugin,目的是为了让其余插件做者区分当前应用的是一个 Android 插件。
project.getPluginManager().apply(AndroidBasePlugin.class);
// 三、检查 project 路径是否有错误,发生错误则抛出 StopExecutionException 异常。
checkPathForErrors();
// 四、检查子 moudle 的结构:目前版本会检查 2 个模块有没有相同的标识(组+名称),若是有则抛出 StopExecutionException 异常。(组件化在不一样的 moudle 中须要给资源加 prefix 前缀)
checkModulesForErrors();
// 五、插件初始化,必须当即执行。此外,须要注意,Gradle Deamon 永远不会同时执行两个构建流程。
PluginInitializer.initialize(project);
// 六、初始化用于记录构建过程当中配置信息的工厂实例 ProcessProfileWriterFactory
RecordingBuildListener buildListener = ProfilerInitializer.init(project, projectOptions);
// 七、给 project 设置 android plugin version、插件类型、插件生成器、project 选项
ProcessProfileWriter.getProject(project.getPath())
.setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION)
.setAndroidPlugin(getAnalyticsPluginType())
.setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST)
.setOptions(AnalyticsUtil.toProto(projectOptions));
// 配置工程
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
project.getPath(),
null,
this::configureProject);
// 配置 Extension
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
project.getPath(),
null,
this::configureExtension);
// 建立 Tasks
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
project.getPath(),
null,
this::createTasks);
}
复制代码
能够看到,前 4 个步骤都是一些检查操做,然后 3 个步骤则是对插件进行初始化与配置。咱们 梳理下 准备工程 中的任务
,以下所示:
插件检查操做
对插件进行初始化与配置相关信息
Plugin 的准备工程完成以后,就会执行 BasePlugin 中的 configureProject 方法进行项目的配置了,其源码以下所示:
private void configureProject() {
...
// 一、建立 DataBindingBuilder 实例。
dataBindingBuilder = new DataBindingBuilder();
dataBindingBuilder.setPrintMachineReadableOutput(
SyncOptions.getErrorFormatMode(projectOptions) == ErrorFormatMode.MACHINE_PARSABLE);
// 二、强制使用不低于当前所支持的最小插件版本,不然会抛出异常。
GradlePluginUtils.enforceMinimumVersionsOfPlugins(project, syncIssueHandler);
// 三、应用 Java Plugin。
project.getPlugins().apply(JavaBasePlugin.class);
// 四、若是启动了 构建缓存 选项,则会建立 buildCache 实例以便后面能重用缓存。
@Nullable
FileCache buildCache = BuildCacheUtils.createBuildCacheIfEnabled(project, projectOptions);
// 五、这个回调将会在整个 project 执行完成以后执行(注意不是在当前 moudle 执行完成以后执行),由于每个 project 都会调用此回调, 因此它可能会执行屡次。
// 在整个 project 构建完成以后,会进行资源回收、缓存清除并关闭在此过程当中全部启动的线程池组件。
gradle.addBuildListener(
new BuildAdapter() {
@Override
public void buildFinished(@NonNull BuildResult buildResult) {
// Do not run buildFinished for included project in composite build.
if (buildResult.getGradle().getParent() != null) {
return;
}
ModelBuilder.clearCaches();
Workers.INSTANCE.shutdown();
sdkComponents.unload();
SdkLocator.resetCache();
ConstraintHandler.clearCache();
CachedAnnotationProcessorDetector.clearCache();
threadRecorder.record(
ExecutionType.BASE_PLUGIN_BUILD_FINISHED,
project.getPath(),
null,
() -> {
if (!projectOptions.get(
BooleanOption.KEEP_SERVICES_BETWEEN_BUILDS)) {
WorkerActionServiceRegistry.INSTANCE
.shutdownAllRegisteredServices(
ForkJoinPool.commonPool());
}
Main.clearInternTables();
});
DeprecationReporterImpl.Companion.clean();
}
});
...
}
复制代码
最后,咱们梳理下 configureProject 中所执行的 五项主要任务,以下所示:
而后,咱们看到 BasePlugin 的 configureExtension 方法,其核心源码以下所示:
private void configureExtension() {
// 一、建立盛放 buildType、productFlavor、signingConfig 的容器实例。
...
final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs =
project.container(BaseVariantOutput.class);
// 二、建立名为 buildOutputs 的扩展属性配置。
project.getExtensions().add("buildOutputs", buildOutputs);
...
// 三、建立 android DSL 闭包。
extension =
createExtension(
project,
projectOptions,
globalScope,
buildTypeContainer,
productFlavorContainer,
signingConfigContainer,
buildOutputs,
sourceSetManager,
extraModelInfo);
// 四、给全局域设置建立好的 android DSL 闭包。
globalScope.setExtension(extension);
// 五、建立一个 ApplicationVariantFactory 实例,以用于生产 APKs。
variantFactory = createVariantFactory(globalScope);
// 六、建立一个 ApplicationTaskManager 实例,负责为 Android 应用工程去建立 Tasks。
taskManager =
createTaskManager(
globalScope,
project,
projectOptions,
dataBindingBuilder,
extension,
variantFactory,
registry,
threadRecorder);
// 七、建立一个 VariantManager 实例,用于去建立与管理 Variant。
variantManager =
new VariantManager(
globalScope,
project,
projectOptions,
extension,
variantFactory,
taskManager,
sourceSetManager,
threadRecorder);
// 八、将 whenObjectAdded callbacks 映射到 singingConfig 容器之中。
signingConfigContainer.whenObjectAdded(variantManager::addSigningConfig);
// 九、若是不是 DynamicFeature(负责添加一个可选的 APK 模块),则会初始化一个 debug signingConfig DSL 对象并设置给默认的 buildType DSL。
buildTypeContainer.whenObjectAdded(
buildType -> {
if (!this.getClass().isAssignableFrom(DynamicFeaturePlugin.class)) {
SigningConfig signingConfig =
signingConfigContainer.findByName(BuilderConstants.DEBUG);
buildType.init(signingConfig);
} else {
// initialize it without the signingConfig for dynamic-features.
buildType.init();
}
variantManager.addBuildType(buildType);
});
// 十、将 whenObjectAdded callbacks 映射到 productFlavor 容器之中。
productFlavorContainer.whenObjectAdded(variantManager::addProductFlavor);
// 十一、将 whenObjectRemoved 映射在容器之中,当 whenObjectRemoved 回调执行时,会抛出 UnsupportedAction 异常。
signingConfigContainer.whenObjectRemoved(
new UnsupportedAction("Removing signingConfigs is not supported."));
buildTypeContainer.whenObjectRemoved(
new UnsupportedAction("Removing build types is not supported."));
productFlavorContainer.whenObjectRemoved(
new UnsupportedAction("Removing product flavors is not supported."));
// 十二、按顺序依次建立 signingConfig debug、buildType debug、buildType release 类型的 DSL。
variantFactory.createDefaultComponents(
buildTypeContainer, productFlavorContainer, signingConfigContainer);
}
复制代码
最后,咱们梳理下 configureExtension 中的任务,以下所示:
其中 最核心的几项处理能够概括为以下 四点:
在 BasePlugin 的 apply 方法最后,调用了 createTasks 方法来建立 Tasks,该方法以下所示:
private void createTasks() {
// 一、在 evaluate 以前建立 Tasks
threadRecorder.record(
ExecutionType.TASK_MANAGER_CREATE_TASKS,
project.getPath(),
null,
() -> taskManager.createTasksBeforeEvaluate());
// 二、建立 Android Tasks
project.afterEvaluate(
CrashReporting.afterEvaluate(
p -> {
sourceSetManager.runBuildableArtifactsActions();
threadRecorder.record(
ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
project.getPath(),
null,
this::createAndroidTasks);
}));
}
复制代码
能够看到,createTasks 分为两种 Task 的建立方式,即 createTasksBeforeEvaluate 与 createAndroidTasks。
下面,咱们来详细分析下其实现过程。
TaskManager 的 createTasksBeforeEvaluate 方法给 Task 容器中注册了一系列的 Task,包括 uninstallAllTask、deviceCheckTask、connectedCheckTask、preBuild、extractProguardFiles、sourceSetsTask、assembleAndroidTest、compileLintTask 等等。
在 BasePlugin 的 createAndroidTasks 方法中主要 是生成 flavors 相关数据,并根据 flavor 建立与之对应的 Task 实例并注册进 Task 容器之中。其核心源码以下所示:
@VisibleForTesting
final void createAndroidTasks() {
// 一、CompileSdkVersion、插件配置冲突检测(如 JavaPlugin、retrolambda)。
// 建立一些基础或通用的 Tasks。
// 二、将 Project Path、CompileSdk、BuildToolsVersion
、Splits、KotlinPluginVersion、FirebasePerformancePluginVersion 等信息写入 Project 的配置之中。
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);
}
AnalyticsUtil.recordFirebasePerformancePluginVersion(project);
// 三、建立应用的 Tasks。
List<VariantScope> variantScopes = variantManager.createAndroidTasks();
// 建立一些基础或通用的 Tasks 与作一些通用的处理。
}
复制代码
在 createAndroidTasks 除了建立一些基础或通用的 Tasks 与作一些通用的处理以外, 主要作了三件事,以下所示:
咱们须要 重点关注 variantManager 的 createAndroidTasks 方法,去核心源码以下所示:
/** Variant/Task creation entry point. */
public List<VariantScope> createAndroidTasks() {
...
// 一、建立工程级别的测试任务。
taskManager.createTopLevelTestTasks(!productFlavors.isEmpty());
// 二、遍历全部 variantScope,为其变体数据建立对应的 Tasks。
for (final VariantScope variantScope : variantScopes) {
createTasksForVariantData(variantScope);
}
// 三、建立报告相关的 Tasks。
taskManager.createReportTasks(variantScopes);
return variantScopes;
}
复制代码
能够看到,在 createAndroidTasks 方法中有 三项处理
,以下所示:
接着,咱们继续看看 createTasksForVariantData 方法是如何为每个指定的 Variant 类型建立对应的 Tasks 的,其核心源码以下所示:
// 为每个指定的 Variant 类型建立与之对应的 Tasks
public void createTasksForVariantData(final VariantScope variantScope) {
final BaseVariantData variantData = variantScope.getVariantData();
final VariantType variantType = variantData.getType();
final GradleVariantConfiguration variantConfig = variantScope.getVariantConfiguration();
// 一、建立 Assemble Task。
taskManager.createAssembleTask(variantData);
// 二、若是 variantType 是 base moudle,则会建立相应的 bundle Task。须要注意的是,base moudle 是指包含功能的 moudle,而用于 test 的 moudle 则是不包含功能的。
if (variantType.isBaseModule()) {
taskManager.createBundleTask(variantData);
}
// 三、若是 variantType 是一个 test moudle(其做为一个 test 的组件),则会建立相应的 test variant。
if (variantType.isTestComponent()) {
// 1)、将 variant-specific, build type multi-flavor、defaultConfig 这些依赖添加到当前的 variantData 之中。
...
// 2)、若是支持渲染脚本,则添加渲染脚本的依赖。
if (testedVariantData.getVariantConfiguration().getRenderscriptSupportModeEnabled()) {
project.getDependencies()
.add(
variantDep.getCompileClasspath().getName(),
project.files(
globalScope
.getSdkComponents()
.getRenderScriptSupportJarProvider()));
}
// 3)、若是当前 Variant 会输出一个 APK,即当前是执行的一个 Android test(通常用来进行 UI 自动化测试),则会建立相应的 AndroidTestVariantTask。
if (variantType.isApk()) { // ANDROID_TEST
if (variantConfig.isLegacyMultiDexMode()) {
String multiDexInstrumentationDep =
globalScope.getProjectOptions().get(BooleanOption.USE_ANDROID_X)
? ANDROIDX_MULTIDEX_MULTIDEX_INSTRUMENTATION
: COM_ANDROID_SUPPORT_MULTIDEX_INSTRUMENTATION;
project.getDependencies()
.add(
variantDep.getCompileClasspath().getName(),
multiDexInstrumentationDep);
project.getDependencies()
.add(
variantDep.getRuntimeClasspath().getName(),
multiDexInstrumentationDep);
}
taskManager.createAndroidTestVariantTasks(
(TestVariantData) variantData,
variantScopes
.stream()
.filter(TaskManager::isLintVariant)
.collect(Collectors.toList()));
} else { // UNIT_TEST
// 4)、不然说明该 Test moudle 是用于执行单元测试的,则会建立 UnitTestVariantTask。 taskManager.createUnitTestVariantTasks((TestVariantData) variantData);
}
} else {
// 四、若是不是一个 Test moudle,则会调用 ApplicationTaskManager 的 createTasksForVariantScope 方法。
taskManager.createTasksForVariantScope(
variantScope,
variantScopes
.stream()
.filter(TaskManager::isLintVariant)
.collect(Collectors.toList()));
}
}
复制代码
在 createTasksForVariantData 方法中为每个指定的 Variant 类型建立了与之对应的 Tasks,该方法的处理逻辑以下所示:
最终,会执行到 ApplicationTaskManager 的 createTasksForVariantScope 方法,在这个方法里面建立了适用于应用构建的一系列 Tasks。
下面,咱们就经过 assembleDebug 的打包流程来分析一下这些 Tasks。
在对 assembleDebug 构建过程当中的一系列 Task 分析以前,咱们须要先回顾一下 Android 的打包流程(对这块很是熟悉的同窗能够跳过)。
Android 官方的编译打包流程图以下所示:
比较粗略的打包流程可简述为以下 四个步骤:
为了 了解更多打包过程当中的细节,咱们须要查看更加详细的旧版 APK 打包流程图 ,以下图所示:
比较详细的打包流程可简述为以下 八个步骤:
至此,咱们已经了解了整个 APK 编译和打包的流程。
主要基于如下 两点缘由
:
一、空间占用更小
:由于全部 XML 元素的标签、属性名称、属性值和内容所涉及到的字符串都会被统一收集到一个字符串资源池中,而且会去重。有了这个字符串资源池,原来使用字符串的地方就会被替换成一个索引到字符串资源池的整数值,从而能够减小文件的大小。
二、解析效率更高
:二进制格式的 XML 文件解析速度更快。这是因为二进制格式的 XML 元素里面再也不包含有字符串值,所以就避免了进行字符串解析,从而提升了解析效率。
主要基于两个文件,以下所示:
资源 ID 文件 R.java
:
赋予每个非 assets 资源一个 ID 值,这些 ID 值以常量的形式定义在 R.java 文件中。
资源索引表 resources.arsc
:
用来描述那些具备 ID 值的资源的配置信息。
咱们能够经过下面的命令来获取打包一个 Debug APK 所须要的执行的 Task,以下所示:
quchao@quchaodeMacBook-Pro CustomPlugin % ./gradlew app:assembleDebug --console=plain
...
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:generateDebugBuildConfig
> Task :app:javaPreCompileDebug
> Task :app:mainApkListPersistenceDebug
> Task :app:generateDebugResValues
> Task :app:createDebugCompatibleScreenManifests
> Task :app:extractDeepLinksDebug
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:generateDebugResources
> Task :app:processDebugManifest
> Task :app:mergeDebugResources
> Task :app:processDebugResources
> Task :app:compileDebugJavaWithJavac
> Task :app:compileDebugSources
> Task :app:mergeDebugShaders
> Task :app:compileDebugShaders
> Task :app:generateDebugAssets
> Task :app:mergeDebugAssets
> Task :app:processDebugJavaRes NO-SOURCE
> Task :app:checkDebugDuplicateClasses
> Task :app:dexBuilderDebug
> Task :app:mergeLibDexDebug
> Task :app:mergeDebugJavaResource
> Task :app:mergeDebugJniLibFolders
> Task :app:validateSigningDebug
> Task :app:mergeProjectDexDebug
> Task :app:mergeDebugNativeLibs
> Task :app:stripDebugDebugSymbols
> Task :app:desugarDebugFileDependencies
> Task :app:mergeExtDexDebug
> Task :app:packageDebug
> Task :app:assembleDebug
复制代码
在 TaskManager 中,主要有两种方法用来去建立 Task,它们分别为 createTasksBeforeEvaluate
方法与 createTasksForVariantScope
方法。须要注意的是,createTasksForVariantScope 方法是一个抽象方法,其具体的建立 Tasks 的任务分发给了 TaskManager 的子类进行处理,其中最多见的子类要数 ApplicationTaskManager 了,它就是在 Android 应用程序中用于建立 Tasks 的 Task 管理者。
其中,打包流程中的大部分 tasks 都在这个目录之下:
com.android.build.gradle.internal.tasks
下面,咱们看看 assembleDebug 打包流程中所需的各个 Task 所对应的实现类与含义,以下表所示:
Task | 对应实现类 | 做用 |
---|---|---|
preBuild | AppPreBuildTask | 预先建立的 task,用于作一些 application Variant 的检查 |
preDebugBuild | 与 preBuild 区别是这个 task 是用于在 Debug 的环境下的一些 Vrariant 检查 | |
generateDebugBuildConfig | GenerateBuildConfig | 生成与构建目标相关的 BuildConfig 类 |
javaPreCompileDebug | JavaPreCompileTask | 用于在 Java 编译以前执行必要的 action |
mainApkListPersistenceDebug | MainApkListPersistence | 用于持久化 APK 数据 |
generateDebugResValues | GenerateResValues | 生成 Res 资源类型值 |
createDebugCompatibleScreenManifests | CompatibleScreensManifest | 生成具备给定屏幕密度与尺寸列表的 (兼容屏幕)节点清单 |
extractDeepLinksDebug | ExtractDeepLinksTask | 用于抽取一系列 DeepLink(深度连接技术,主要应用场景是经过Web页面直接调用Android原生app,而且把须要的参数经过Uri的形式,直接传递给app,节省用户的注册成本) |
compileDebugAidl | AidlCompile | 编译 AIDL 文件 |
compileDebugRenderscript | RenderscriptCompile | 编译 Renderscript 文件 |
generateDebugResources | 在 TaskManager.createAnchorTasks 方法中经过 taskFactory.register(taskName)的方式注册一个 task | 空 task,锚点 |
processDebugManifest | ProcessApplicationManifest | 处理 manifest 文件 |
mergeDebugResources | MergeResources | 使用 AAPT2 合并资源文件 |
processDebugResources | ProcessAndroidResources | 用于处理资源并生成 R.class 文件 |
compileDebugJavaWithJavac | JavaCompileCreationAction(这里是一个 Action,从 gradle 源码中能够看到从 TaskFactory 中注册一个 Action 能够获得与之对应的 Task,所以,Task 即 Action,Action 即 Task) | 用于执行 Java 源码的编译 |
compileDebugSources | 在 TaskManager.createAnchorTasks 方法中经过 taskFactory.register(taskName)的方式注册一个 task | 空 task,锚点使用 |
mergeDebugShaders | MergeSourceSetFolders.MergeShaderSourceFoldersCreationAction | 合并 Shader 文件 |
compileDebugShaders | ShaderCompile | 编译 Shaders |
generateDebugAssets | 在 TaskManager.createAnchorTasks 方法中经过 taskFactory.register(taskName)的方式注册一个 task | 空 task,锚点 |
mergeDebugAssets | MergeSourceSetFolders.MergeAppAssetCreationAction | 合并 assets 文件 |
processDebugJavaRes | ProcessJavaResConfigAction | 处理 Java Res 资源 |
checkDebugDuplicateClasses | CheckDuplicateClassesTask | 用于检测工程外部依赖,确保不包含重复类 |
dexBuilderDebug | DexArchiveBuilderTask | 用于将 .class 文件转换成 dex archives,即 DexArchive,Dex 存档,能够经过 addFile 添加一个 DEX 文件 |
mergeLibDexDebug | DexMergingTask.DexMergingAction.MERGE_LIBRARY_PROJECT | 仅仅合并库工程中的 DEX 文件 |
mergeDebugJavaResource | MergeJavaResourceTask | 合并来自多个 moudle 的 Java 资源 |
mergeDebugJniLibFolders | MergeSourceSetFolders.MergeJniLibFoldersCreationAction | 以合适的优先级合并 JniLibs 源文件夹 |
validateSigningDebug | ValidateSigningTask | 用于检查当前 Variant 的签名配置中是否存在密钥库文件,若是当前密钥库默认是 debug keystore,即便它不存在也会进行相应的建立 |
mergeProjectDexDebug | DexMergingTask.DexMergingAction.MERGE_PROJECT | 仅仅合并工程的 DEX 文件 |
mergeDebugNativeLibs | MergeNativeLibsTask | 从多个 moudle 中合并 native 库 |
stripDebugDebugSymbols | StripDebugSymbolsTask | 从 Native 库中移除 Debug 符号。 |
desugarDebugFileDependencies | DexFileDependenciesTask | 处理 Dex 文件的依赖关系 |
mergeExtDexDebug | DexMergingTask.DexMergingAction.MERGE_EXTERNAL_LIBS | 仅仅用于合并外部库的 DEX 文件 |
packageDebug | PackageApplication | 打包 APK |
assembleDebug | Assemble | 空 task,锚点使用 |
目前,在 Gradle Plugin 中主要有三种类型的 Task,以下所示:
增量 Task
:
继承于 NewIncrementalTask 这个增量 Task 基类,须要重写 doTaskAction 抽象方法实现增量功能。
非增量 Task
:
继承于 NonIncrementalTask 这个非增量 Task 基类,重写 doTaskAction 抽象方法实现全量更新功能。
Transform Task
:
咱们编写的每个自定义 Transform 会在调用 appExtension.registerTransform(new CustomTransform()) 注册方法时将其保存到当前的 Extension 类中的 transforms 列表中,当 LibraryTaskManager/TaskManager 调用 createPostCompilationTasks(负责为给定 Variant 建立编译后的 task)方法时,会取出相应 Extension 中的 tranforms 列表进行遍历,并经过 TransformManager.addTransform 方法将每个 Transform 转换为与之对应的 TransformTask 实例,而该方法内部具体是经过 new TransformTask.CreationAction(...) 的形式进行建立。
全面了解了打包过程当中涉及到的一系列 Tasks 与 Task 必备的一些基础知识以后,咱们再来对其中最重要的几个 Task 的实现来进行详细分析。
processDebugManifest 对应的实现类为 ProcessApplicationManifest Task,它继承了 IncrementalTask,可是没有实现 isIncremental 方法,所以咱们只需看其 doFullTaskAction 方法便可。
processDebugManifest.dofFullTaskAction => ManifestHelperKt.mergeManifestsForApplication => ManifestMerge2.merge
复制代码
这个 task 功能主要是 用于合并全部的(包括 module 和 flavor) mainfest,其过程主要是利用 MergingReport,ManifestMerger2 和 XmlDocument 这三个实例进行处理。
咱们直接关注到 ManifestMerger2.merge 方法的 merge 过程,看看具体的合并是怎样的。其主体步骤以下所示:
// load the main manifest file to do some checking along the way.
LoadedManifestInfo loadedMainManifestInfo =
load(
new ManifestInfo(
mManifestFile.getName(),
mManifestFile,
mDocumentType,
Optional.absent() /* mainManifestPackageName*/),
selectors,
mergingReportBuilder);
复制代码
// perform system property injection
performSystemPropertiesInjection(mergingReportBuilder,
loadedMainManifestInfo.getXmlDocument());
复制代码
/**
* Perform {@link ManifestSystemProperty} injection.
* @param mergingReport to log actions and errors.
* @param xmlDocument the xml document to inject into.
*/
protected void performSystemPropertiesInjection(
@NonNull MergingReport.Builder mergingReport,
@NonNull XmlDocument xmlDocument) {
for (ManifestSystemProperty manifestSystemProperty : ManifestSystemProperty.values()) {
String propertyOverride = mSystemPropertyResolver.getValue(manifestSystemProperty);
if (propertyOverride != null) {
manifestSystemProperty.addTo(
mergingReport.getActionRecorder(), xmlDocument, propertyOverride);
}
}
}
复制代码
for (File inputFile : mFlavorsAndBuildTypeFiles) {
mLogger.verbose("Merging flavors and build manifest %s \n", inputFile.getPath());
LoadedManifestInfo overlayDocument =
load(
new ManifestInfo(
null,
inputFile,
XmlDocument.Type.OVERLAY,
mainPackageAttribute.transform(it -> it.getValue())),
selectors,
mergingReportBuilder);
if (!mFeatureName.isEmpty()) {
overlayDocument =
removeDynamicFeatureManifestSplitAttributeIfSpecified(
overlayDocument, mergingReportBuilder);
}
// 一、检查 package 定义
Optional<XmlAttribute> packageAttribute =
overlayDocument.getXmlDocument().getPackage();
// if both files declare a package name, it should be the same.
if (loadedMainManifestInfo.getOriginalPackageName().isPresent() &&
packageAttribute.isPresent()
&& !loadedMainManifestInfo.getOriginalPackageName().get().equals(
packageAttribute.get().getValue())) {
// 二、若是 package 定义重复的话,会输出下面信息
String message = mMergeType == MergeType.APPLICATION
? String.format(
"Overlay manifest:package attribute declared at %1$s value=(%2$s)\n"
+ "\thas a different value=(%3$s) "
+ "declared in main manifest at %4$s\n"
+ "\tSuggestion: remove the overlay declaration at %5$s "
+ "\tand place it in the build.gradle:\n"
+ "\t\tflavorName {\n"
+ "\t\t\tapplicationId = \"%2$s\"\n"
+ "\t\t}",
packageAttribute.get().printPosition(),
packageAttribute.get().getValue(),
mainPackageAttribute.get().getValue(),
mainPackageAttribute.get().printPosition(),
packageAttribute.get().getSourceFile().print(true))
: String.format(
"Overlay manifest:package attribute declared at %1$s value=(%2$s)\n"
+ "\thas a different value=(%3$s) "
+ "declared in main manifest at %4$s",
packageAttribute.get().printPosition(),
packageAttribute.get().getValue(),
mainPackageAttribute.get().getValue(),
mainPackageAttribute.get().printPosition());
mergingReportBuilder.addMessage(
overlayDocument.getXmlDocument().getSourceFile(),
MergingReport.Record.Severity.ERROR,
message);
return mergingReportBuilder.build();
}
...
}
复制代码
for (LoadedManifestInfo libraryDocument : loadedLibraryDocuments) {
mLogger.verbose("Merging library manifest " + libraryDocument.getLocation());
xmlDocumentOptional = merge(
xmlDocumentOptional, libraryDocument, mergingReportBuilder);
if (!xmlDocumentOptional.isPresent()) {
return mergingReportBuilder.build();
}
}
复制代码
performPlaceHolderSubstitution(
loadedMainManifestInfo,
xmlDocumentOptional.get(),
mergingReportBuilder,
severity);
复制代码
mergeDebugResources 对应的是 MergeResources Task,它 使用了 AAPT2 合并资源。
MergeResources.doFullTaskAction => ResourceMerger.mergeData => MergedResourceWriter.end => mResourceCompiler.submitCompile => AaptV2CommandBuilder.makeCompileCommand
复制代码
MergeResources 继承自 IncrementalTask,对于 增量 Task 来讲咱们只需看以下三个方法的实现:
isIncremental
doFullTaskAction
doIncrementalTaskAction
// 说明 MergeResources Task 支持增量,确定重写了 doIncrementalTaskAction 方法
protected boolean isIncremental() {
return true;
}
复制代码
List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor);
复制代码
ResourceMerger merger = new ResourceMerger(minSdk.get());
复制代码
// makeAapt 中使用 aapt2,而后返回 ResourceCompilationService 实例.
ResourceCompilationService resourceCompiler =
getResourceProcessor(
getAapt2FromMaven(),
workerExecutorFacade,
errorFormatMode,
flags,
processResources,
getLogger())) {
复制代码
for (ResourceSet resourceSet : resourceSets) {
resourceSet.loadFromFiles(new LoggerWrapper(getLogger()));
merger.addDataSet(resourceSet);
}
复制代码
MergedResourceWriter writer =
new MergedResourceWriter(
workerExecutorFacade,
destinationDir,
publicFile,
mergingLog,
preprocessor,
resourceCompiler,
getIncrementalFolder(),
dataBindingLayoutProcessor,
mergedNotCompiledResourcesOutputDirectory,
pseudoLocalesEnabled,
getCrunchPng());
复制代码
merger.mergeData(writer, false /*doCleanUp*/);
复制代码
// MergedResourceWriter.end()
mResourceCompiler.submitCompile(
new CompileResourceRequest(
fileToCompile,
request.getOutputDirectory(),
request.getInputDirectoryName(),
request.getInputFileIsFromDependency(),
pseudoLocalesEnabled,
crunchPng,
ImmutableMap.of(),
request.getInputFile()));
mCompiledFileMap.put(
fileToCompile.getAbsolutePath(),
mResourceCompiler.compileOutputFor(request).getAbsolutePath());
复制代码
在 submitCompile 中最终会使用 AaptV2CommandBuilder.makeCompileCommand 方法生成 aapt2 命令去处理资源。
即 dexBuilderDebug,它具体对应的是 DexArchiveBuilderTask,用于将 .class 文件转换成 dex archives,即 Dex 存档,它能够经过 addFile 添加一个 DEX 文件。
DexArchiveBuilderTask.doTaskAction => DexArchiveBuilderTaskDelegate.doProcess => DexArchiveBuilderTaskDelegate.processClassFromInput => DexArchiveBuilderTaskDelegate.convertToDexArchive -> DexArchiveBuilderTaskDelegate.launchProcessing -> DexArchiveBuilder.convert
复制代码
在 DexArchiveBuilderTask 中,对 class 的处理方式分为两种,一种是对 目录下的 class 进行处理,一种是对 .jar 里面的 class 进行处理。
由于 .jar 中的 class 文件一般来讲都是依赖库,基本上不会改变,因此 gradle 在这里就能够实现一个缓存操做。
在处理 jar 包的时候,Gradle 会对 jar 包中的每个 class 文件都单独打成一个 DEX 文件,而后再把它们放回 jar 包之中。
private fun convertJarToDexArchive(
jarInput: File,
outputDir: File,
bootclasspath: ClasspathServiceKey,
classpath: ClasspathServiceKey,
cacheInfo: D8DesugaringCacheInfo
): List<File> {
if (cacheInfo !== DesugaringDontCache) {
val cachedVersion = cacheHandler.getCachedVersionIfPresent(
jarInput, cacheInfo.orderedD8DesugaringDependencies
)
if (cachedVersion != null) {
// 若是有缓存,直接使用缓存的 jar 包。
val outputFile = getOutputForJar(jarInput, outputDir, null)
Files.copy(
cachedVersion.toPath(),
outputFile.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
// no need to try to cache an already cached version.
return listOf()
}
}
// 若是没有缓存,则调用 convertToDexArchive 方法去生成 dex。
return convertToDexArchive(
jarInput,
outputDir,
false,
bootclasspath,
classpath,
setOf(),
setOf()
)
}
复制代码
内部会调用 launchProcessing 对 dir 进行处理,代码以下所示:
private fun launchProcessing(
dexConversionParameters: DexArchiveBuilderTaskDelegate.DexConversionParameters,
outStream: OutputStream,
errStream: OutputStream,
receiver: MessageReceiver
) {
val dexArchiveBuilder = dexConversionParameters.getDexArchiveBuilder(
outStream,
errStream,
receiver
)
val inputPath = dexConversionParameters.input.toPath()
val hasIncrementalInfo =
dexConversionParameters.input.isDirectory && dexConversionParameters.isIncremental
// 若是 class 新增 || 修改过,就进行处理
fun toProcess(path: String): Boolean {
if (!dexConversionParameters.belongsToThisBucket(path)) return false
if (!hasIncrementalInfo) {
return true
}
val resolved = inputPath.resolve(path).toFile()
return resolved in dexConversionParameters.additionalPaths || resolved in dexConversionParameters.changedFiles
}
val bucketFilter = { name: String -> toProcess(name) }
loggerWrapper.verbose("Dexing '" + inputPath + "' to '" + dexConversionParameters.output + "'")
try {
ClassFileInputs.fromPath(inputPath).use { input ->
input.entries(bucketFilter).use { entries ->
// 内部会调用 dx || d8 去生成 dex 文件
dexArchiveBuilder.convert(
entries,
Paths.get(URI(dexConversionParameters.output)),
dexConversionParameters.input.isDirectory
)
}
}
} catch (ex: DexArchiveBuilderException) {
throw DexArchiveBuilderException("Failed to process $inputPath", ex)
}
}
复制代码
能够看到,在 DexArchiveBuilder 有两个子类,它们分别以下所示:
D8DexArchiveBuilder
:
调用 D8 去生成 DEX 文件。
DxDexArchiveBuilder
:
调用 DX 去生成 DEX 文件。
咱们这里就以 D8DexArchiveBuilder 为例来讲明其是如何调用 D8 去生成 DEX 文件的。其源码以下所示:
@Override
public void convert(
@NonNull Stream<ClassFileEntry> input, @NonNull Path output, boolean isIncremental)
throws DexArchiveBuilderException {
// 一、建立一个 D8 诊断信息处理器实例,用于发出不一样级别的诊断信息,共分为三类,由严重程度递减分别为:error、warning、info。
D8DiagnosticsHandler d8DiagnosticsHandler = new InterceptingDiagnosticsHandler();
try {
// 二、建立一个 D8 命令构建器实例。
D8Command.Builder builder = D8Command.builder(d8DiagnosticsHandler);
AtomicInteger entryCount = new AtomicInteger();
// 三、遍历读取每个类的字节数据。
input.forEach(
entry -> {
builder.addClassProgramData(
readAllBytes(entry), D8DiagnosticsHandler.getOrigin(entry));
entryCount.incrementAndGet();
});
if (entryCount.get() == 0) {
// 三、若是没有可遍历的数据,则直接 return。这里使用 AtomicInteger 类来实现了是否有遍历了数据的区分处理。
return;
}
OutputMode outputMode =
isIncremental ? OutputMode.DexFilePerClassFile : OutputMode.DexIndexed;
// 四、给 D8 命令构建器实例设置一系列的配置,例如 编译模式、最小 Sdk 版本等等。
builder.setMode(compilationMode)
.setMinApiLevel(minSdkVersion)
.setIntermediate(true)
.setOutput(output, outputMode)
.setIncludeClassesChecksum(compilationMode == compilationMode.DEBUG);
if (desugaring) {
builder.addLibraryResourceProvider(bootClasspath.getOrderedProvider());
builder.addClasspathResourceProvider(classpath.getOrderedProvider());
if (libConfiguration != null) {
builder.addSpecialLibraryConfiguration(libConfiguration);
}
} else {
builder.setDisableDesugaring(true);
}
// 五、使用 com.android.tools.r8 工具包中的 D8 类的 run 方法运行组装后的 D8 命令。
D8.run(builder.build(), MoreExecutors.newDirectExecutorService());
} catch (Throwable e) {
throw getExceptionToRethrow(e, d8DiagnosticsHandler);
}
}
复制代码
D8DexArchiveBuilder 的 convert 过程能够概括为 五个步骤
,以下所示:
咱们再回头看看开篇时的那一幅 Gradle 插件的总体实现架构图,以下所示:
咱们能够 根据上面这幅图,由下而上细细地思考回忆一下,每一层的主要流程是什么?其中涉及的一些关键细节具体有哪些?此时,你是否以为已经真正地理解了 Gradle 插件架构的实现原理呢?
一、Android Gradle Plugin V3.6.2 源码
二、Gradle V5.6.4 源码
三、Android Plugin DSL Reference
七、连载 | 深刻理解Gradle框架之一:Plugin, Extension, buildSrc
九、连载 | 深刻理解gradle框架之三:artifacts的发布
十、Android Gradle Plugin 源码解析(上)
十一、Android Gradle Plugin 源码解析(下)
1三、Gradle 庖丁解牛(构建生命周期核心委托对象建立源码浅析)
欢迎关注个人微信:
bcce5360
因为微信群已超过 200 人,麻烦你们想进微信群的朋友们,加我微信拉你进群。
2千人QQ群,Awesome-Android学习交流群,QQ群号:959936182, 欢迎你们加入~