文章目录
前面我们已经详细讲解了
Gradle
的
Task、Project
等基本用法,现在我们还要学习一个很重要的概念
Extension
,它在
Gradle
中几乎随处可见,特别是在
Android
打包配置中。
一 什么是Extension
我们先来看一段 Android
应用的 Gradle
配置代码:
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.hm.iou.thinapk.demo"
minSdkVersion 19
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'
}
}
}
相信做 Android
应用开发的同学,对这段代码都快看吐了吧。记得当初刚从 eclipse
转到 Android Studio
的时候,看这些配置就像看天书一样,只知道按规定配置就可以了。但是为什么要这样配置?除此外还支持哪些配置?为什么一定要在 android
这个命名空间下配置呢?可以不可以定义自己的特殊配置呢?
上面这个 android
打包配置,就是 Gradle
的 Extension
,翻译成中文意思就叫扩展
。它的作用就是通过实现自定义的 Extension
,可以在 Gradle
脚本中增加类似 android
这样命名空间的配置,Gradle
可以识别这种配置,并读取里面的配置内容。
二 定义Extension
2.1 ExtensionContainer
一般我们通过 ExtensionContainer
来创建 Extension
,这个类与 TaskContainer
命名有点类似,TaskContainer
是用来创建并管理 Task
的,而 ExtensionContainer
则是用来创建并管理 Extension
的。通过 Project
的以下 API
可以获取到 ExtensionContainer
对象:
ExtensionContainer getExtensions()
2.2 简单的Extension
//先定义一个普通的java类,包含2个属性
class Foo {
int age
String username
String toString() {
return "name = ${username}, age = ${age}"
}
}
//创建一个名为 foo 的Extension
getExtensions().create("foo", Foo)
//配置Extension
foo {
age = 30
username = "hjy"
}
task testExt << {
//能直接通过 project 获取到自定义的 Extension
println project.foo
}
上面这个例子中,foo
就是我们自定义的 Extension
了,它里面能配置的属性与类 Foo
中的字段是一致的,在 build.gradle
中可以直接通过 project.foo
来访问。每个 Extension
实际上与某个类是相关联的,在 build.gradle
中通过 DSL
来定义,Gradle
会识别解析并生成一个对象实例,通过该类可以获取我们所配置的信息。
之前有讲过 Project
有个扩展属性是通过 ext
命名空间配置的,可以看到 ext
与这里是类似的,不同的是 ext
可以配置任何键值对的属性值,而这里只能识别我们定义的 Java
类里的属性值。
2.3 ExtensionContainer主要API功能及用法
2.3.1 创建Extension
<T> T create(String name, Class<T> type, Object... constructionArguments)
<T> T create(Class<T> publicType, String name, Class<? extends T> instanceType, Object... constructionArguments)
先来看看后面这个 API
所有参数的含义。
- publicType:创建的
Extension
实例暴露出来的类类型; - name:要创建的
Extension
的名字,可以是任意符合命名规则的字符串,不能与已有的重复,否则会抛异常; - instanceType:该
Extension
的类类型; - constructionArguments:类的构造函数参数值
官方文档里还说明了一个特性,创建的 Extension
对象都默认实现了 ExtensionAware
接口,
The new instance will have been dynamically made ExtensionAware, which means that you can cast it to ExtensionAware.
我们来看一个具体的实例,包含了上面2个 API 的使用:
//父类
class Animal {
String username
int legs
Animal(String name) {
username = name
}
void setLegs(int c) {
legs = c
}
String toString() {
return "This animal is $username, it has ${legs} legs."
}
}
//子类
class Pig extends Animal {
int age
String owner
Pig(int age, String owner) {
super("Pig")
this.age = age
this.owner = owner
}
String toString() {
return super.toString() + " Its age is $age, its owner is $owner."
}
}
//创建的Extension是 Animal 类型
Animal aAnimal = getExtensions().create(Animal, "animal", Pig, 3, "hjy")
//创建的Extension是 Pig 类型
Pig aPig = getExtensions().create("pig", Pig, 5, "kobe")
animal {
legs = 4 //配置属性
}
pig {
setLegs 2 //这个是方法调用,也就是 setLegs(2)
}
task testExt << {
println aAnimal
println aPig
//验证 aPig 对象是 ExtensionAware 类型的
println "aPig is a instance of ExtensionAware : ${aPig instanceof ExtensionAware}"
}
运行 testExt 这个任务,查看结果如下:
This animal is Pig, it has 4 legs. Its age is 3, its owner is hjy.
This animal is Pig, it has 2 legs. Its age is 5, its owner is kobe.
aPig is a instance of ExtensionAware : true
2.3.2 增加Extension
前面的 create()
方法会创建并返回一个 Extension
对象,与之相似的还有一个 add()
方法,唯一的差别是它并不会返回一个 Extension
对象。
void add(Class<T> publicType, String name, T extension)
void add(String name, T extension)
基于前面的这个实例,我们可以换一种写法如下:
getExtensions().add(Pig, "mypig", new Pig(5, "kobe"))
mypig {
username = "MyPig"
legs = 4
age = 1
}
task testExt << {
def aPig = project.getExtensions().getByName("mypig")
println aPig
}
2.3.3 查找Extension
- Object findByName(String name)
- <T> T findByType(Class<T> type)
- Object getByName(String name) //找不到会抛异常
- <T> T getByType(Class<T> type) //找不到会抛异常
这几个 API
很好理解,一个是通过名字去查找,一个是通过类类型去查找。
2.4 嵌套Extension
类似下面这样的配置应该随处可见:
outer {
outerName "outer"
msg "this is a outer message."
inner {
innerName "inner"
msg "This is a inner message."
}
}
形式上就是外面的 Extension
里面定义了另一个 Extension
,这种叫做 nested Extension
,也就是嵌套的 Extension
。本文开头的 Android
打包配置,就是采用的这种方式。
那怎么创建上面这种 Extension
呢?
class OuterExt {
String outerName
String msg
InnerExt innerExt = new InnerExt()
void outerName(String name) {
outerName = name
}
void msg(String msg) {
this.msg = msg
}
//创建内部Extension,名称为方法名 inner
void inner(Action<InnerExt> action) {
action.execute(inner)
}
//创建内部Extension,名称为方法名 inner
void inner(Closure c) {
org.gradle.util.ConfigureUtil.configure(c, innerExt)
}
String toString() {
return "OuterExt[ name = ${outerName}, msg = ${msg}] " + innerExt
}
}
class InnerExt {
String innerName
String msg
void innerName(String name) {
innerName = name
}
void msg(String msg) {
this.msg = msg
}
String toString() {
return "InnerExt[ name = ${innerName}, msg = ${msg}]"
}
}
def outExt = getExtensions().create("outer", OuterExt)
outer {
outerName "outer"
msg "this is a outer message."
inner {
innerName "inner"
msg "This is a inner message."
}
}
task testExt << {
println outExt
}
运行结果如下:
OuterExt[ name = outer, msg = this is a outer message.] InnerExt[ name = inner, msg = This is a inner message.]
这里的关键点在于下面这2个方法的定义,只需要定义任意一个即可:
void inner(Action<InnerExt> action)
void inner(Closure c)
定义在 outer
内部的 inner
,Gradle
解析时实质上会进行方法调用,也就是会执行 outer.inner(…)
方法,而该方法的参数是一个闭包(俗称 Script Block
),所以在类 OuterExt
中必须定义 inner(…)
方法。
此外,前面说到创建的 Extension
对象都是实现了 ExtensionAware
接口的,ExtensionAware
接口很简单,只包含一个方法:
ExtensionContainer getExtensions()
所以还有一种方式来创建嵌套的 Extension
,只不过这种方式没法自动赋值到 OuterExt
类里的 innerExt
对象:
def innerExt = outExt.getExtensions().create("inner", InnerExt)
三 Android的Extension
先看个 Android
的常规配置,以下是我的项目配置,截图如下所示:
我们重点看看 defaultConfig
、productFlavors
、signingConfigs
、buildTypes
这4个内部 Extension
对象是怎么定义的,通过查看源码可以找到一个叫 BaseExtension
的类,里面的相关代码如下:
private final DefaultConfig defaultConfig;
private final NamedDomainObjectContainer<ProductFlavor> productFlavors;
private final NamedDomainObjectContainer<BuildType> buildTypes;
private final NamedDomainObjectContainer<SigningConfig> signingConfigs;
public void defaultConfig(Action<DefaultConfig> action) {
this.checkWritability();
action.execute(this.defaultConfig);
}
public void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action) {
this.checkWritability();
action.execute(this.buildTypes);
}
public void productFlavors(Action<? super NamedDomainObjectContainer<ProductFlavor>> action) {
this.checkWritability();
action.execute(this.productFlavors);
}
public void signingConfigs(Action<? super NamedDomainObjectContainer<SigningConfig>> action) {
this.checkWritability();
action.execute(this.signingConfigs);
}
这与前面介绍的嵌套 Extension
的定义是一致的,这里名为 android
的 Extension
是通过插件来创建的。
在 app
的 build.gradle
里我们通常会采用插件 apply plugin: ‘com.android.application’
,而在 library module
中则采用插件 apply plugin: ‘com.android.library’
,先来看一张截图:
图中类 AppPlugin
就是插件 com.android.application
的实现类,LibraryPlugin
则是插件 com.android.library
的实现类,接着再看看 AppPlugin
里是怎样创建 Extension
的:
public class AppPlugin extends BasePlugin implements Plugin<Project> {
@Inject
public AppPlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) {
super(instantiator, registry);
}
protected BaseExtension createExtension(Project project, ProjectOptions projectOptions, Instantiator instantiator, AndroidBuilder androidBuilder, SdkHandler sdkHandler, NamedDomainObjectContainer<BuildType> buildTypeContainer, NamedDomainObjectContainer<ProductFlavor> productFlavorContainer, NamedDomainObjectContainer<SigningConfig> signingConfigContainer, NamedDomainObjectContainer<BaseVariantOutput> buildOutputs, ExtraModelInfo extraModelInfo) {
return (BaseExtension)project.getExtensions().create("android", AppExtension.class, new Object[]{
project, projectOptions, instantiator, androidBuilder, sdkHandler, buildTypeContainer, productFlavorContainer, signingConfigContainer, buildOutputs, extraModelInfo});
}
public void apply(Project project) {
super.apply(project);
}
//省略...
}
在 createExtension()
方法中,可以看到创建了一个名为 android
的 Extension
,该 Extension
的类型为 AppExtension
,而 AppExtension
的继承结构为 AppExtension -> TestedExtension -> BaseExtension
,所以它的实现逻辑大部分都是在 BaseExtension
里实现的。
LibraryExtension
的继承结构与 AppExtension
基本是一致的,有兴趣的可以自己看源码研究研究。
以后当我们不知道 android
里有哪些配置时,除了查看 API
文档以外,还可以直接翻看 BaseExtension
源码,基本上就能清楚了。