一、前言
上一篇文章中我们讲到了Android Plugin中的apply方法中最为重要的三个回调方法,分别是configureProject、configureExtension、createTasks。而且还分析了configureProject回调方法中的逻辑,详见Android Plugin源码与Gradle构建(一)。今天我们就接着上面的思路,继续分析Android Plugin的源码。
二、初识Extension
我们先来看一段熟悉的build.gradle脚本:
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.example.runningh.mydemo"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
signingConfigs {
release {
storeFile file(release_storeFile)
storePassword release_storePassword
keyAlias release_keyAlias
keyPassword release_keyPassword
}
}
dataBinding {
enabled true
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
相信大家对上述的build.gradle脚本应该很熟悉了。它是使用groovy语言编写的,上面这段脚本配置了项目的minSdkVersion
(项目最低可运行在哪个版本的SDK手机上)、targetSdkVersion
(项目已经在哪个版本的SDK上做了优化)、versionCode(版本号)、versionName
(版本名)、signingConfigs
(签名)、buildTypes
(构建配置)等信息。
然而,为什么要这样写呢?我们自定义一个插件(关于自定义插件,可以参考这篇文章在AndroidStudio中自定义Gradle插件)叫做MyPlugin:
class MyPlugin implements Plugin<Project>{
@Override
void apply(Project project) {
project.extensions.add("book", Book)
project.task('bookInfo') {
group 'myplugin'
doLast {
Book ext = project.book
println ext
}
}
}
}
apply
方法中的第一行调用了project.extensions
的add
方法将Book这个类加入到project
的extension
中,通过“book"(记住这个名字,后面需要在build.gradle中用到)这个名字可以引用到Book
类。project
的extension
可以看做是一个容器。
apply
方法的第二行定义了一个名为“bookInfo”的task
,并将其放入到了名为“myplugin”的group中,然后该任务最后执行了将该Book
类信息打印出来的逻辑。
我们看一下Book类是怎么定义的:
public class Book {
String bookName;
int price;
@Override
public String toString() {
return "BookName is $bookName, price is $price."
}
}
很简单,定义了两个变量,分别为bookName、price
,并重写了toString
方法,所以上面的MyPlugin插件直接调用println
方法可直接执行Book
的toString
方法。
最后我们需要项目的build.gradle中配置book(还记得这个名字吗?我们通过project.extension
的add
方法将该名字作为其中的一个参数):
book {
bookName 'Android开发’
price 100
}
看到这里,是不是和上面的android标签相类似呢?其实这种配置方法相当于给book做了初始化,相当于我们可以动态配置Book
类的变量。最后我们执行一下bookInfo任务,执行结果如下:
BookName is Android开发, price is 100.
三、Android插件的Extension
private void configureExtension() {
ObjectFactory objectFactory = project.getObjects();
final NamedDomainObjectContainer<BuildType> buildTypeContainer =
project.container(
BuildType.class,
new BuildTypeFactory(
objectFactory,
project,
extraModelInfo.getSyncIssueHandler(),
extraModelInfo.getDeprecationReporter()));
final NamedDomainObjectContainer<ProductFlavor> productFlavorContainer =
project.container(
ProductFlavor.class,
new ProductFlavorFactory(
objectFactory,
project,
extraModelInfo.getDeprecationReporter(),
project.getLogger()));
final NamedDomainObjectContainer<SigningConfig> signingConfigContainer =
project.container(
SigningConfig.class,
new SigningConfigFactory(
objectFactory,
GradleKeystoreHelper.getDefaultDebugKeystoreLocation()));
final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs =
project.container(BaseVariantOutput.class);
project.getExtensions().add("buildOutputs", buildOutputs);
sourceSetManager = createSourceSetManager();
extension =
createExtension(
project,
projectOptions,
androidBuilder,
sdkHandler,
buildTypeContainer,
productFlavorContainer,
signingConfigContainer,
buildOutputs,
sourceSetManager,
extraModelInfo);
ndkHandler =
new NdkHandler(
project.getRootDir(),
null, /* compileSkdVersion, this will be set in afterEvaluate */
"gcc",
"" /*toolchainVersion*/,
false /* useUnifiedHeaders */);
@Nullable
FileCache buildCache = BuildCacheUtils.createBuildCacheIfEnabled(project, projectOptions);
GlobalScope globalScope =
new GlobalScope(
project,
projectOptions,
androidBuilder,
extension,
sdkHandler,
ndkHandler,
registry,
buildCache);
variantFactory = createVariantFactory(globalScope, androidBuilder, extension);
taskManager =
createTaskManager(
globalScope,
project,
projectOptions,
androidBuilder,
dataBindingBuilder,
extension,
sdkHandler,
ndkHandler,
registry,
threadRecorder);
variantManager =
new VariantManager(
globalScope,
project,
projectOptions,
androidBuilder,
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 -> {
SigningConfig signingConfig =
signingConfigContainer.findByName(BuilderConstants.DEBUG);
buildType.init(signingConfig);
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);
}
调用project.container
方法创建了一个类型为NamedDomainObjectContainer< BuildType >
的对象buildTypeContainer
,查看Gradle的API文档,可以看到container
方法的作用:
大概的意思是创建了一个容器来管理泛型中定义的类,这些类可以使用factory工厂来创建。
所以上面创建了buildType
、productFlavor
、signingConfig
、BaseVariantOutput
这四个类的容器。
然后调用createExtension
方法,该方法是抽象方法,具体在AppPlugin类中实现,如下所示:
protected BaseExtension createExtension(
@NonNull Project project,
@NonNull ProjectOptions projectOptions,
@NonNull AndroidBuilder androidBuilder,
@NonNull SdkHandler sdkHandler,
@NonNull NamedDomainObjectContainer<BuildType> buildTypeContainer,
@NonNull NamedDomainObjectContainer<ProductFlavor> productFlavorContainer,
@NonNull NamedDomainObjectContainer<SigningConfig> signingConfigContainer,
@NonNull NamedDomainObjectContainer<BaseVariantOutput> buildOutputs,
@NonNull SourceSetManager sourceSetManager,
@NonNull ExtraModelInfo extraModelInfo) {
return project.getExtensions()
.create(
"android",
AppExtension.class,
project,
projectOptions,
androidBuilder,
sdkHandler,
buildTypeContainer,
productFlavorContainer,
signingConfigContainer,
buildOutputs,
sourceSetManager,
extraModelInfo);
}
这里又创建了一个Extension,名为“android”,类型为AppExtension,传递的参数有project、androidBuildersourceSetManager、extraModleInfo
和上面创建的四个Container对象。
如果我们再回过头来看最上面我们提到的build.gradle中的android标签,其实android标签就对应着这里的“android”的Extension,然后android标签中可以配置compileSdkVersion
,并且嵌套了defaultConfig、buildTypes、signingConfigs
等标签,这些标签就有上面提到的四个Container对象。
最后还创建了variantFactory、taskManager、variantManager
,其中variantFactory
为构建信息的工厂、taskManager
为构建任务管理、variantManager
构建方式与多渠道构建管理。
四、总结
本文讲述了Android Plugin中的Extension,通过源码追踪的方式讲述了android标签、标签中的嵌套标签是怎么实现的,让我们在看build.gradle的配置信息时不至于一脸懵逼。
接下来的一篇文章,我们将讲到Android Plugin中的createTasks回调方法的逻辑。