本文基于的gradle版本如下:
plugin版本 :com.android.tools.build:gradle:3.5.4'
gradle 版本:5.6.4
我们知道Android gradle plugin是用来构建Android工程的gradle插件,在Android gradle 插件中,可以看到app工程和library工程所依赖的plugin是不一样的。
// app 工程
apply plugin: 'com.android.application'
// library 工程
apply plugin: 'com.android.library'
首先我们来看一个app的gradle文件格式。
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.example.flavorgradle"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
flavorDimensions "default"
productFlavors {
out1 {
applicationId "com.example.out1"
buildConfigField "String", "CHANNEL", '"channel_out1"'
versionCode 1
versionName "1.0"
signingConfig signingConfigs.release
}
out2 {
applicationId "com.example.out2"
buildConfigField "String", "CHANNEL", '"channel_out2"'
versionCode 1
versionName "1.0"
signingConfig signingConfigs.release
}
out3 {
applicationId "com.example.out3"
buildConfigField "String", "CHANNEL", '"channel_out2"'
versionCode 1
versionName "1.0"
signingConfig signingConfigs.release
}
}
signingConfigs {
debug {
//签名文件
storeFile file(keystoreProperties["STORE_FILE"])
storePassword keystoreProperties["STORE_PASSWORD"]
keyAlias keystoreProperties["KEY_ALIAS"]
//签名密码
keyPassword keystoreProperties["KEY_PASSWORD"]
v2SigningEnabled false
}
release {
//签名文件
storeFile file(keystoreProperties["STORE_FILE"])
storePassword keystoreProperties["STORE_PASSWORD"]
keyAlias keystoreProperties["KEY_ALIAS"]
//签名密码
keyPassword keystoreProperties["KEY_PASSWORD"]
v2SigningEnabled false
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
/*
这个需要跟上面productFlavors配合使用,目的是只是在项目为out3打包的时候引用,编译
*/
out3Implementation 'com.github.vivitale:BaseCore:0.0.54'
}
我们都知道最上面引用的plugin id 对应gradle3.5.4的META-INF/gradle-plugins文件夹的com.android.build.gradle.properties文件。
implementation-class=com.android.build.gradle.AppPlugin
对应具体的插件为AppPlugin。
然后android则对应AppExtension。其创建是在AbstractAppPlugin类中。
protected BaseExtension createExtension(
@NonNull Project project,
@NonNull ProjectOptions projectOptions,
@NonNull GlobalScope globalScope,
@NonNull NamedDomainObjectContainer buildTypeContainer,
@NonNull NamedDomainObjectContainer productFlavorContainer,
@NonNull NamedDomainObjectContainer signingConfigContainer,
@NonNull NamedDomainObjectContainer buildOutputs,
@NonNull SourceSetManager sourceSetManager,
@NonNull ExtraModelInfo extraModelInfo) {
return project.getExtensions()
.create(
"android",
getExtensionClass(),
project,
projectOptions,
globalScope,
buildTypeContainer,
productFlavorContainer,
signingConfigContainer,
buildOutputs,
sourceSetManager,
extraModelInfo,
isBaseApplication);
}
创建了一个名字为android的Extension,具体实现为AppExtension。
public AppExtension(
@NonNull Project project,
@NonNull ProjectOptions projectOptions,
@NonNull GlobalScope globalScope,
@NonNull NamedDomainObjectContainer buildTypes,
@NonNull NamedDomainObjectContainer productFlavors,
@NonNull NamedDomainObjectContainer signingConfigs,
@NonNull NamedDomainObjectContainer buildOutputs,
@NonNull SourceSetManager sourceSetManager,
@NonNull ExtraModelInfo extraModelInfo,
boolean isBaseModule) {
super(
project,
projectOptions,
globalScope,
buildTypes,
productFlavors,
signingConfigs,
buildOutputs,
sourceSetManager,
extraModelInfo,
isBaseModule);
}
其中signingConfigs对应build.gradle文件中的
signingConfigs {
debug {
//签名文件
storeFile file(keystoreProperties["STORE_FILE"])
storePassword keystoreProperties["STORE_PASSWORD"]
keyAlias keystoreProperties["KEY_ALIAS"]
//签名密码
keyPassword keystoreProperties["KEY_PASSWORD"]
v2SigningEnabled false
}
release {
//签名文件
storeFile file(keystoreProperties["STORE_FILE"])
storePassword keystoreProperties["STORE_PASSWORD"]
keyAlias keystoreProperties["KEY_ALIAS"]
//签名密码
keyPassword keystoreProperties["KEY_PASSWORD"]
v2SigningEnabled false
}
}
这一整块代码。buildTypes则对应:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
productFlavors对应
productFlavors {
out1 {
applicationId "com.example.out1"
buildConfigField "String", "CHANNEL", '"channel_out1"'
versionCode 1
versionName "1.0"
signingConfig signingConfigs.release
}
out2 {
applicationId "com.example.out2"
buildConfigField "String", "CHANNEL", '"channel_out2"'
versionCode 1
versionName "1.0"
signingConfig signingConfigs.release
}
out3 {
applicationId "com.example.out3"
buildConfigField "String", "CHANNEL", '"channel_out2"'
versionCode 1
versionName "1.0"
signingConfig signingConfigs.release
}
}
下面我们来分析AppPlugin的构建流程。AppPlugin最终会走父类BasePlgin的apply方法。
public final void apply(@NonNull Project project) {
CrashReporting.runAction(
() -> {
basePluginApply(project);
pluginSpecificApply(project);
});
}
private void basePluginApply(@NonNull Project project) {
this.project = project;
this.projectOptions = new ProjectOptions(project);
checkGradleVersion(project, getLogger(), projectOptions);//1
DependencyResolutionChecks.registerDependencyCheck(project, projectOptions);//2
project.getPluginManager().apply(AndroidBasePlugin.class);//3
checkPathForErrors();//4
checkModulesForErrors();//5
PluginInitializer.initialize(project);//5
RecordingBuildListener buildListener = ProfilerInitializer.init(project, projectOptions);//6
ProfileAgent.INSTANCE.register(project.getName(), buildListener);
threadRecorder = ThreadRecorder.get();
Workers.INSTANCE.initFromProject(
projectOptions,
ForkJoinPool.commonPool());
ProcessProfileWriter.getProject(project.getPath())
.setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION)
.setAndroidPlugin(getAnalyticsPluginType())
.setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST)
.setOptions(AnalyticsUtil.toProto(projectOptions));//7
if (!projectOptions.get(BooleanOption.ENABLE_NEW_DSL_AND_API)) {
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
project.getPath(),
null,
this::configureProject);//8
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
project.getPath(),
null,
this::configureExtension);//9
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
project.getPath(),
null,
this::createTasks);//10
} else {
......
}
}
注释1检查gradle版本。
注释2检查并确保在配置阶段不去解析依赖。
注释3应用一个 AndroidBasePlugin,目的是为了让其他插件作者区分当前应用的是一个 Android 插件。AndroidBasePlugin是一个空实现。
注释4是检查 project 路径是否有错误,发生错误则抛出 StopExecutionException 异常。
注释5检查子 moudle 的结构:目前版本会检查 2 个模块有没有相同的标识(组+名称),如果有则抛出 StopExecutionException 异常。(组件化在不同的 moudle 中需要给资源加 prefix 前缀)
注释6插件初始化,必须立即执行。此外,需要注意,Gradle Deamon进程 永远不会同时执行两个构建流程。
(Gradle会构建两个进程,一个是client进程,其作用是查找并与Deamon进程通信。当Deamon进程没有开启,Client进程则会创建一个Deamon进程。如果Deamon进程已经开启,则将参数给Deamon进程。而Deamon进程是不依赖AS独立存在,构建结束不会依赖。每一个Gradle版本都会对应一个Deamon进程。这么做的目的是加速编译构建时间。)
注释7初始化用于记录构建过程中配置信息的工厂实例 ProcessProfileWriterFactory。
现在重点来分析注释8,9,10 。
注释8:
private void configureProject() {
final Gradle gradle = project.getGradle();
......
// Enforce minimum versions of certain plugins
//强制使用不低于当前所支持的最小插件版本,否则会抛出异常。
GradlePluginUtils.enforceMinimumVersionsOfPlugins(project, syncIssueHandler);
// Apply the Java plugin
project.getPlugins().apply(JavaBasePlugin.class);
DslScopeImpl dslScope =
new DslScopeImpl(
syncIssueHandler, extraModelInfo.getDeprecationReporter(), objectFactory);
//如果启动了 构建缓存 选项,则会创建 buildCache 实例以便后面能重用缓存。
@Nullable
FileCache buildCache = BuildCacheUtils.createBuildCacheIfEnabled(project, projectOptions);
globalScope =
new GlobalScope(
project,
creator,
new ProjectWrapper(project),
projectOptions,
dslScope,
sdkComponents,
registry,
buildCache,
extraModelInfo.getMessageReceiver());
//给名字为assemble的添加描述。
project.getTasks()
.named("assemble")
.configure(
task ->
task.setDescription(
"Assembles all variants of all applications and secondary packages."));
//对构建各个阶段的监听
gradle.addBuildListener(
new BuildListener() {
//构建开始
@Override
public void buildStarted(@NonNull Gradle gradle) {}
//settings 文件解析完成
@Override
public void settingsEvaluated(@NonNull Settings settings) {}
//project加载完成
@Override
public void projectsLoaded(@NonNull Gradle gradle) {}
//project解析完成
@Override
public void projectsEvaluated(@NonNull Gradle gradle) {}
//构建完成
@Override
public void buildFinished(@NonNull BuildResult buildResult) {
if (buildResult.getGradle().getParent() != null) {
return;
}
ModelBuilder.clearCaches();
Workers.INSTANCE.shutdown();
sdkComponents.unload();
SdkLocator.resetCache();
ConstraintHandler.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();
}
});
createLintClasspathConfiguration(project);
}
根据这些回调,我们可以做一些事,比如在我们的settings.gradle文件中去测量每个阶段所用的时间。
long beginOfSetting = System.currentTimeMillis()
def beginOfConfig
def configHasBegin = false
def beginOfProjectConfig = new HashMap()
def beginOfProjectExcute
gradle.projectsLoaded {
println '初始化阶段,耗时:' + (System.currentTimeMillis() -
beginOfSetting) + 'ms'
}
gradle.beforeProject { project ->
if (!configHasBegin) {
configHasBegin = true
beginOfConfig = System.currentTimeMillis()
}
beginOfProjectConfig.put(project, System.currentTimeMillis())
}
gradle.afterProject { project ->
def begin = beginOfProjectConfig.get(project)
println '配置阶段,' + project + '耗时:' +
(System.currentTimeMillis() - begin) + 'ms'
}
gradle.taskGraph.whenReady {
println '配置阶段,总共耗时:' + (System.currentTimeMillis() -
beginOfConfig) + 'ms'
beginOfProjectExcute = System.currentTimeMillis()
}
gradle.taskGraph.beforeTask { task ->
task.doFirst {
task.ext.beginOfTask = System.currentTimeMillis()
}
task.doLast {
println '执行阶段,' + task + '耗时:' +
(System.currentTimeMillis() - task.beginOfTask) + 'ms'
}
}
gradle.buildFinished {
println '执行阶段,耗时:' + (System.currentTimeMillis() -
beginOfProjectExcute) + 'ms'
}
下面来看注释9
private void configureExtension() {
ObjectFactory objectFactory = project.getObjects();
final NamedDomainObjectContainer buildTypeContainer =
project.container(
BuildType.class,
new BuildTypeFactory(
objectFactory,
project,
extraModelInfo.getSyncIssueHandler(),
extraModelInfo.getDeprecationReporter()));
final NamedDomainObjectContainer productFlavorContainer =
project.container(
ProductFlavor.class,
new ProductFlavorFactory(
objectFactory,
project,
extraModelInfo.getDeprecationReporter(),
project.getLogger()));
final NamedDomainObjectContainer signingConfigContainer =
project.container(
SigningConfig.class,
new SigningConfigFactory(
objectFactory,
GradleKeystoreHelper.getDefaultDebugKeystoreLocation()));
final NamedDomainObjectContainer buildOutputs =
project.container(BaseVariantOutput.class);
project.getExtensions().add("buildOutputs", buildOutputs);
sourceSetManager =
new SourceSetManager(
project,
isPackagePublished(),
globalScope.getDslScope(),
new DelayedActionsExecutor());
extension =
createExtension(
project,
projectOptions,
globalScope,
buildTypeContainer,
productFlavorContainer,
signingConfigContainer,
buildOutputs,
sourceSetManager,
extraModelInfo);
globalScope.setExtension(extension);
variantFactory = createVariantFactory(globalScope, extension);
taskManager =
createTaskManager(
globalScope,
project,
projectOptions,
dataBindingBuilder,
extension,
variantFactory,
registry,
threadRecorder);
variantManager =
new VariantManager(
globalScope,
project,
projectOptions,
extension,
variantFactory,
taskManager,
sourceSetManager,
threadRecorder);
registerModels(registry, globalScope, variantManager, extension, extraModelInfo);
// map the whenObjectAdded callbacks on the containers.
signingConfigContainer.whenObjectAdded(variantManager::addSigningConfig);
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);
});
productFlavorContainer.whenObjectAdded(variantManager::addProductFlavor);
// map whenObjectRemoved on the containers to throw an exception.
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."));
// create default Objects, signingConfig first as its used by the BuildTypes.
variantFactory.createDefaultComponents(
buildTypeContainer, productFlavorContainer, signingConfigContainer);
}
首先创建了 BuildType、ProductFlavor、SigningConfig、buildOutputs 四个类型的Container,接着传入到了createExtension方法中,该方法的具体实现是在AbstractAppPlugin。
对于Extension我们可以理解为java中的bean类。而BuildType、ProductFlavor、SigningConfig、buildOutputs属于这个bean类里面的成员变量。来看一个具体的SigningConfig。
public SigningConfig initWith(com.android.builder.model.SigningConfig that) {
setStoreFile(that.getStoreFile());
setStorePassword(that.getStorePassword());
setKeyAlias(that.getKeyAlias());
setKeyPassword(that.getKeyPassword());
setV1SigningEnabled(that.isV1SigningEnabled());
setV2SigningEnabled(that.isV2SigningEnabled());
setStoreType(that.getStoreType());
return this;
}
其具体的实现类对应SigningConfigImpl,
override fun initWith(that: SigningConfig) {
if (checkSeal()) {
storeFile = that.storeFile
storePassword = that.storePassword
keyAlias = that.keyAlias
keyPassword = that.keyPassword
v1SigningEnabled = that.v1SigningEnabled
v2SigningEnabled = that.v2SigningEnabled
}
}
而这些对应于build.gradle的:
signingConfigs {
debug {
//签名文件
storeFile file(keystoreProperties["STORE_FILE"])
storePassword keystoreProperties["STORE_PASSWORD"]
keyAlias keystoreProperties["KEY_ALIAS"]
//签名密码
keyPassword keystoreProperties["KEY_PASSWORD"]
v2SigningEnabled false
}
release {
//签名文件
storeFile file(keystoreProperties["STORE_FILE"])
storePassword keystoreProperties["STORE_PASSWORD"]
keyAlias keystoreProperties["KEY_ALIAS"]
//签名密码
keyPassword keystoreProperties["KEY_PASSWORD"]
v2SigningEnabled false
}
}
一般打包出来的apk需要签名才能安装在我们的手机上。但是我们的debug好像直接就可以安装,也不需要配置签名什么的。其实在源码中,当是debug包的时候,有一个默认的签名。
public static DefaultSigningConfig debugSigningConfig(File storeFile) {
DefaultSigningConfig result = new DefaultSigningConfig(BuilderConstants.DEBUG);
result.mStoreFile = storeFile;
result.mStorePassword = DEFAULT_PASSWORD;
result.mKeyAlias = DEFAULT_ALIAS;
result.mKeyPassword = DEFAULT_PASSWORD;
return result;
}
这个storeFile是从外面传进来的,我们继续跟下去。
fun createDefaultDebugStore(defaultDebugKeystoreLocation: File, logger: Logger) {
val signingConfig = DefaultSigningConfig.debugSigningConfig(defaultDebugKeystoreLocation)
logger.info(
"Creating default debug keystore at {}",
defaultDebugKeystoreLocation.absolutePath)
try {
if (!KeystoreHelper.createDebugStore(
signingConfig.storeType,
signingConfig.storeFile!!,
signingConfig.storePassword!!,
signingConfig.keyPassword!!,
signingConfig.keyAlias!!,
LoggerWrapper(logger))) {
throw IOException("Unable to create missing debug keystore.")
}
} catch (e: KeytoolException) {
throw IOException(e)
}
}
private fun createDefaultDebugKeystoreIfNeeded() {
checkState(signingConfig.isSigningReady, "Debug signing config not ready.")
checkState(FileUtils.parentDirExists(defaultDebugKeystoreLocation),
"Parent directory of the default debug keystore '%s' does not exist",
defaultDebugKeystoreLocation)
SynchronizedFile
.getInstanceWithMultiProcessLocking(defaultDebugKeystoreLocation)
.createIfAbsent { createDefaultDebugStore(it, this.logger) }
}
createDefaultDebugKeystoreIfNeeded中的defaultDebugKeystoreLocation这个赋值是在CreationAction内部类中,在构造方法中传入一个defaultDebugKeystoreLocation,然后在configure进行赋值。
override fun configure(task: ValidateSigningTask) {
super.configure(task)
task.signingConfig = variantScope.variantConfiguration.signingConfig ?: throw IllegalStateException(
"No signing config configured for variant " + variantScope.fullVariantName)
task.defaultDebugKeystoreLocation = defaultDebugKeystoreLocation
task.dummyOutputDirectory = outputDirectory
task.outputs.upToDateWhen { !task.forceRerun() }
}
继续往下跟进。这个内部类是在一个task中,而我们的task是手TaskManager统一管理。所以可以在TaskManager找到这个内部类的实现。
taskFactory.register(
new ValidateSigningTask.CreationAction(
variantScope, GradleKeystoreHelper.getDefaultDebugKeystoreLocation()));
fun getDefaultDebugKeystoreLocation(): File = try {
File(KeystoreHelper.defaultDebugKeystoreLocation())
} catch (e: AndroidLocation.AndroidLocationException) {
throw InvalidUserDataException("Failed to get default debug keystore location.", e)
}
public static String defaultDebugKeystoreLocation() throws AndroidLocationException {
//this is guaranteed to either return a non null value (terminated with a platform
// specific separator), or throw.
String folder = AndroidLocation.getFolder();
return folder + "debug.keystore";
}
最后找到的是debug.keystore的一个路径。
这里只是分析了SigningConfig,其他的后面有时间再详细分析。
最后来分析注释10.
private void createTasks() {
threadRecorder.record(
ExecutionType.TASK_MANAGER_CREATE_TASKS,
project.getPath(),
null,
() -> taskManager.createTasksBeforeEvaluate());
project.afterEvaluate(
CrashReporting.afterEvaluate(
p -> {
sourceSetManager.runBuildableArtifactsActions();
threadRecorder.record(
ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
project.getPath(),
null,
this::createAndroidTasks);
}));
}
这里主要是分两块,一个是在 beforeEvaluate 创建任务;一个是在 afterEvaluate 创建任务。这里的区别是AndroidTask是依赖配置项的配置才能生成相应任务,所以是需要在 afterEvaluate 之后创建。
createTasksBeforeEvaluate 创建不依赖 flavor 的 task,TaskManager 的 createTasksBeforeEvaluate 方法给 Task 容器中注册了一系列的 Task,包括 uninstallAllTask、deviceCheckTask、connectedCheckTask、preBuild、extractProguardFiles、sourceSetsTask、assembleAndroidTest、compileLintTask 等等。
我们来看看对应的gradle task,如下图所示:
Android 任务
androidDependencies-显示项目的Android依赖项
signingReport-显示基础和测试模块的签名信息
sourceSets-打印出此项目中定义的所有源集
Build 任务
assemble-组装所有变体的主要输出
assembleAndroidTest-组装所有测试应用程序
build-组装并测试该项目
buildDependents-组装并测试该项目以及依赖于该项目的所有项目
buildNeeded-组装和测试该项目及其依赖的所有项目
bundle-为所有变体组装捆绑包
clean-删除构建目录
cleanBuildCache-删除构建缓存目录
compileDebugAndroidTestSources
compileDebugSources
compileDebugUnitTestSources
compileReleaseSources
compileReleaseUnitTestSources
构建安装任务
init-初始化一个新的Gradle构建
wrapper-生成Gradle包装器文件
清理任务
lintFix-在所有变体上运行lint并对源代码应用任何安全建议
帮助任务
buildEnvironment-显示在根项目“”中声明的所有buildscript依赖项
components-显示根项目“”生成的组件
dependencies-显示在根项目“”中声明的所有依赖项
dependencyInsight-显示对根项目“”中特定依赖关系的洞察。
help-显示帮助消息
model-显示根项目“”的配置模型
projects-显示根项目“”的子项目。
安装任务
installDebug-安装Debug构建
installDebugAndroidTest-为Debug版本安装android(在设备上)测试
uninstallAll-卸载所有应用程序。
uninstallDebug-卸载Debug构建
uninstallDebugAndroidTest-卸载Debug版本的android(在设备上)测试
uninstallRelease-卸载Release版本
验证任务
check-运行所有检查
connectedAndroidTest-安装并运行连接设备上所有风格的检测测试
connectedCheck-在当前连接的设备上运行所有设备检查
connectedDebugAndroidTest-在连接的设备上安装并运行测试以进行调试
deviceAndroidTest-使用所有设备提供程序安装和运行检测测试
deviceCheck-使用设备提供程序和测试服务器运行所有设备检查
lint-在所有变种上运行lint
test-对所有变体运行单元测试。
testDebugUnitTest-运行调试版本的单元测试
testReleaseUnitTest-运行发布版本的单元测试
重点关注 variantManager 的 createAndroidTasks 方法,
public List createAndroidTasks() {
...
// 1、创建工程级别的测试任务。
taskManager.createTopLevelTestTasks(!productFlavors.isEmpty());
// 2、遍历所有 variantScope,为其变体数据创建对应的 Tasks。
for (final VariantScope variantScope : variantScopes) {
createTasksForVariantData(variantScope);
}
// 3、创建报告相关的 Tasks。
taskManager.createReportTasks(variantScopes);
return variantScopes;
}
可以看到,在 createAndroidTasks 方法中有 三项处理,如下所示:
创建工程级别的测试任务。
遍历所有的 variantScope,为其变体数据创建对应的 Tasks。
创建报告相关的 Tasks。
继续看看 createTasksForVariantData 方法是如何为每一个指定的 Variant 类型创建对应的 Tasks 的。
public void createTasksForVariantData(final VariantScope variantScope) {
final BaseVariantData variantData = variantScope.getVariantData();
final VariantType variantType = variantData.getType();
final GradleVariantConfiguration variantConfig = variantScope.getVariantConfiguration();
// 1、创建 Assemble Task。
taskManager.createAssembleTask(variantData);
// 2、如果 variantType 是 base moudle,则会创建相应的 bundle Task。需要注意的是,base moudle 是指包含功能的 moudle,而用于 test 的 moudle 则是不包含功能的。
if (variantType.isBaseModule()) {
taskManager.createBundleTask(variantData);
}
// 3、如果 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 {
// 4、如果不是一个 Test moudle,则会调用 ApplicationTaskManager 的 createTasksForVariantScope 方法。
taskManager.createTasksForVariantScope(
variantScope,
variantScopes
.stream()
.filter(TaskManager::isLintVariant)
.collect(Collectors.toList()));
}
}
在 createTasksForVariantData 方法中为每一个指定的 Variant 类型创建了与之对应的 Tasks,该方法的处理逻辑如下所示:
1、创建 Assemble Task。
2、如果 variantType 是 base moudle,则会创建相应的 bundle Task。需要注意的是,base moudle 是指包含功能的 moudle,而用于 test 的 moudle 则是不包含功能的。
3、如果 variantType 是一个 test moudle(其作为一个 test 的组件),则会创建相应的 test variant。
1)、将 variant-specific, build type multi-flavor、defaultConfig 这些依赖添加到当前的 variantData 之中。
2)、如果支持渲染脚本,则添加渲染脚本的依赖。
3)、如果当前 Variant 会输出一个 APK,即当前是执行的一个 Android test(一般用来进行 UI 自动化测试),则会创建相应的 AndroidTestVariantTask。
4)、否则说明该 Test moudle 是用于执行单元测试的,则会创建 UnitTestVariantTask。
4、如果不是一个 Test moudle,则会调用 ApplicationTaskManager 的 createTasksForVariantScope 方法。
public void createTasksForVariantScope(
@NonNull final VariantScope variantScope,
@NonNull List variantScopesForLint) {
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.register(new MainApkListPersistence.CreationAction(variantScope));
createBuildArtifactReportTask(variantScope);
// Add a task to process the manifest(s)
createMergeApkManifestsTask(variantScope);
// Add a task to create the res values
createGenerateResValuesTask(variantScope);
// Add a task to compile renderscript files.
createRenderscriptTask(variantScope);
// Add a task to merge the resource folders
createMergeResourcesTask(
variantScope,
true,
Sets.immutableEnumSet(MergeResources.Flag.PROCESS_VECTOR_DRAWABLES));
// Add tasks to compile shader
createShaderTask(variantScope);
// Add a task to merge the asset folders
createMergeAssetsTask(variantScope);
// Add a task to create the BuildConfig class
createBuildConfigTask(variantScope);
// Add a task to process the Android Resources and generate source files
createApkProcessResTask(variantScope);
// Add a task to process the java resources
createProcessJavaResTask(variantScope);
createAidlTask(variantScope);
// Add external native build tasks
createExternalNativeBuildJsonGenerators(variantScope);
createExternalNativeBuildTasks(variantScope);
// Add a task to merge the jni libs folders
createMergeJniLibFoldersTasks(variantScope);
// Add feature related tasks if necessary
if (variantScope.getType().isBaseModule()) {
// Base feature specific tasks.
taskFactory.register(new FeatureSetMetadataWriterTask.CreationAction(variantScope));
createValidateSigningTask(variantScope);
// Add a task to produce the signing config file.
taskFactory.register(new SigningConfigWriterTask.CreationAction(variantScope));
if (extension.getDataBinding().isEnabled()) {
// Create a task that will package the manifest ids(the R file packages) of all
// features into a file. This file's path is passed into the Data Binding annotation
// processor which uses it to known about all available features.
//
// see: {@link TaskManager#setDataBindingAnnotationProcessorParams(VariantScope)}
taskFactory.register(
new DataBindingExportFeatureApplicationIdsTask.CreationAction(
variantScope));
}
} else {
// Non-base feature specific task.
// Task will produce artifacts consumed by the base feature
taskFactory.register(
new FeatureSplitDeclarationWriterTask.CreationAction(variantScope));
if (extension.getDataBinding().isEnabled()) {
// Create a task that will package necessary information about the feature into a
// file which is passed into the Data Binding annotation processor.
taskFactory.register(
new DataBindingExportFeatureInfoTask.CreationAction(variantScope));
}
taskFactory.register(new MergeConsumerProguardFilesTask.CreationAction(variantScope));
}
// Add data binding tasks if enabled
createDataBindingTasksIfNecessary(variantScope, MergeType.MERGE);
// Add a compile task
createCompileTask(variantScope);
createStripNativeLibraryTask(taskFactory, variantScope);
if (variantScope.getVariantData().getMultiOutputPolicy().equals(MultiOutputPolicy.SPLITS)) {
if (extension.getBuildToolsRevision().getMajor() < 21) {
throw new RuntimeException(
"Pure splits can only be used with buildtools 21 and later");
}
createSplitTasks(variantScope);
}
createPackagingTask(variantScope);
maybeCreateLintVitalTask(
(ApkVariantData) variantScope.getVariantData(), variantScopesForLint);
// Create the lint tasks, if enabled
createLintTasks(variantScope, variantScopesForLint);
taskFactory.register(new FeatureSplitTransitiveDepsWriterTask.CreationAction(variantScope));
createDynamicBundleTask(variantScope);
}
我们可以聊聊变体。
在build.gradle里面可以使用flavorDimensions属性创建“mode”变种维度和“api”变种维度。
android {
...
buildTypes {
debug {...}
release {...}
}
flavorDimensions "api", "mode"
productFlavors {
demo {
// Assigns this product flavor to the "mode" flavor dimension.
dimension "mode"
...
}
full {
dimension "mode"
...
}
minApi24 {
dimension "api"
minSdkVersion 24
versionCode 30000 + android.defaultConfig.versionCode
versionNameSuffix "-minApi24"
...
}
minApi23 {
dimension "api"
minSdkVersion 23
versionCode 20000 + android.defaultConfig.versionCode
versionNameSuffix "-minApi23"
...
}
minApi21 {
dimension "api"
minSdkVersion 21
versionCode 10000 + android.defaultConfig.versionCode
versionNameSuffix "-minApi21"
...
}
}
}
Gradle 创建的 build 变体数量等于每个变种维度中的变种数量与您配置的 build 类型数量的乘积。当 Gradle 为每个 build 变体或对应的 APK 命名时,先显示属于较高优先级变种维度的产品变种,接着是较低优先级维度中的产品变种,再接着是 build 类型。以上面的构建配置为例,Gradle 使用以下命名方案创建了总共 12 个 build 变体:
build 变体:[minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
对应的 APK:app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk