本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
从Android Plugin源码开始彻底理解gradle构建:初识AndroidDSL(一)
一、前言回顾
上回我们说到Android plugin的所有自定义plugin都需要重写的apply方法,并讲到该方法里最为重要的三个回调方法,我们回顾一下:
//plugin的基础设置、初始化工作
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);
//plugin的task创建
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
project.getPath(),
null,
this::createTasks);
上一篇文章我们简单讲解第一个初始化方法,今天我们的主角就是第二个configureExtension方法
Extension究竟是什么?就我理解,可以把gradle的Extension比喻为JAVA的成员变量。虽然不完全相同,但是也有相似之处,何出此言?让我们继续往下看。
二、Extension介绍
讲解之前我们先看一段熟悉的代码:
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.example.gradletest"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
相信这段代码在座的各位都看吐了吧?
是的,笔者也一样。刚开始接触AS的时候,看这段代码感觉就感觉在看天书,反正按规定配置就对了。今天我们就把他扒光了看看有什么特别。
仔细观察其实也比较好理解,就是android的一些配置:
compileSdkVersion指的是当前的androidSDK版本、applicationId指的是包名、versionCode指的是版本号....balabala...
虽然经过几年的百度、谷歌熏陶已经很熟悉了,但是为什么这么配置呢?接下来就为各位看官解答。
我们可以把一个gradle项目看做一个Project,而这个Project就类似JAVA的类,而一个类自然就需要成员变量咯,所以我们首先就来创建一个pojo类,就命名为Person吧:
public class Person {
String name;
int age;
@Override
public String toString() {
return "I am $name, $age years old"
}
}
然后就像上篇文章那样创建一个自定义插件:
class MyPlugin implements Plugin<Project>{
@Override
void apply(Project project) {
project.extensions.add("person", Person)
project.task('printPerson') {
group 'atom'
doLast{
Person ext = project.person
println ext
}
}
}
}
第一句调用了project的extensions,他可以视为变量的容器,只要往里加就OK了,然后就可以通过project调用到person了
第二句是我们下节需要讲到的内容,可以暂时无视,直接看dolast里面的代码就好了
很简单,我直接打印了该person,接下来就是重点了,我们需要在build.gradle里面配置person
person{
name 'atom'
age 18
}
是不是就像android的标签一样?这下应该很好理解gradle里为何这么写了吧。
其实这里就相当于我们给person做了初始化,只是用有些像json的写法而已。
不过这样我们就可以配置项目参数了,是不是很方便?
我们来验证一下,执行一下gradle printPerson命令:
如我们所想,打印了我们配置的18岁的atom:)
三、Android的Extension
private void configureExtension() {
ObjectFactory objectFactory = project.getObjects();
//1==============
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();
//2==============
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);
//3===============================
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);
//省略部分代码
// create default Objects, signingConfig first as its used by the BuildTypes.
variantFactory.createDefaultComponents(
buildTypeContainer, productFlavorContainer, signingConfigContainer);
}
1、首先创建了四个NamedDomainObjectContainer,是由project的Container方法返回的,这些东东是什么有什么用,光靠百度谷歌基本上是很难找到了(笔者写这篇文章的时候gradle方面的资料还是相当匮乏的),所以我们得学会看官方文档咯~
也不算太难,笔者这二流英语水平都能看懂:创建一个容器,用来管理泛型中定义的类。而factory自然就是创建该类的工厂了。
所以根据上诉代码,不难知道创建了BuildType、ProductFlavor、SigningConfig、BaseVariantOutput这四个类的容器了。
稍微熟悉构建的童鞋应该很清楚这几个类的用处:
buildType
构建类型,在Android Gradle工程中,它已经帮我们内置了debug和release两个构建类型,可以分别设置不同包名等信息。
signingConfigs
签名配置,可以设置debug和release甚至自定义方式时的不同keystore,及其密码等信息。
ProductFlavor
多渠道打包必备,用处很多笔者也有推荐文章介绍
而最后一个BaseVariantOutput,“望文生义“不难才到就是输出文件咯~
2、把project、androidBuilder以及刚刚提到的几个类作为参数创建了extension,这里使用了策略模式,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的方式一样,通过project创建了名为“android”的Extension,类型为AppExtension,这个类就包含了我们平时用到的版本号、包名等等信息,为我们构建项目打下了基础。
3、用同样的方式创建了variantFactory、taskManager、variantManager,最后设置了默认的构建信息。
关于这几个类的作用分别是:
variantFactory构建信息的工厂、taskManager构建任务、variantManager各种不同构建方式及多渠道构建的管理
这就涉及到gradle核心:task了,也是下一篇文章需要讲的内容,大家敬请关注。
四、结语
本文主要介绍了build中配置信息写法的由来,让大家看到配置信息时不再那么没有底气。
不知道大家有没有发现,我例子中使用的groovy语法来写插件,而Android Plugin确是使用Java来写的
具体原因不明,但是可以看出,groovy是完全兼容Java的,如果你想的话甚至可以使用kotlin来写,最新版本的AndroidDSL也加入了kotlin代码。但是还是推荐学习groovy的基本用法,毕竟也有喜欢使用groovy来写插件的,我们得看的懂对吧,比如virtualAPK~