前面我們已經(jīng)詳細(xì)講解了 Gradle 的 Task、Project 等基本用法,現(xiàn)在我們還要學(xué)習(xí)一個(gè)很重要的概念 Extension,它在 Gradle 中幾乎隨處可見(jiàn),特別是在 Android 打包配置中。
1. 什么是Extension
我們先來(lái)看一段 Android 應(yīng)用的 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 應(yīng)用開(kāi)發(fā)的同學(xué),對(duì)這段代碼都快看吐了吧。記得當(dāng)初剛從 eclipse 轉(zhuǎn)到 Android Studio 的時(shí)候,看這些配置就像看天書一樣,只知道按規(guī)定配置就可以了。但是為什么要這樣配置?除此外還支持哪些配置?為什么一定要在 android 這個(gè)命名空間下配置呢?可以不可以定義自己的特殊配置呢?
上面這個(gè) android 打包配置,就是 Gradle 的 Extension,翻譯成中文意思就叫擴(kuò)展。它的作用就是通過(guò)實(shí)現(xiàn)自定義的 Extension,可以在 Gradle 腳本中增加類似 android 這樣命名空間的配置,Gradle 可以識(shí)別這種配置,并讀取里面的配置內(nèi)容。
2. 怎么定義Extension
2.1 ExtensionContainer
一般我們通過(guò) ExtensionContainer 來(lái)創(chuàng)建 Extension,這個(gè)類與 TaskContainer 命名有點(diǎn)類似,TaskContainer 是用來(lái)創(chuàng)建并管理 Task 的,而 ExtensionContainer 則是用來(lái)創(chuàng)建并管理 Extension 的。通過(guò) Project 的以下 API 可以獲取到 ExtensionContainer 對(duì)象:
ExtensionContainer getExtensions()
2.2 簡(jiǎn)單的Extension
//先定義一個(gè)普通的java類,包含2個(gè)屬性
class Foo {
int age
String username
String toString() {
return "name = ${username}, age = ${age}"
}
}
//創(chuàng)建一個(gè)名為 foo 的Extension
getExtensions().create("foo", Foo)
//配置Extension
foo {
age = 30
username = "hjy"
}
task testExt << {
//能直接通過(guò) project 獲取到自定義的 Extension
println project.foo
}
上面這個(gè)例子中,foo 就是我們自定義的 Extension 了,它里面能配置的屬性與類 Foo 中的字段是一致的,在 build.gradle 中可以直接通過(guò) project.foo 來(lái)訪問(wèn)。每個(gè) Extension 實(shí)際上與某個(gè)類是相關(guān)聯(lián)的,在 build.gradle 中通過(guò) DSL 來(lái)定義,Gradle 會(huì)識(shí)別解析并生成一個(gè)對(duì)象實(shí)例,通過(guò)該類可以獲取我們所配置的信息。
之前有講過(guò) Project 有個(gè)擴(kuò)展屬性是通過(guò) ext 命名空間配置的,可以看到 ext 與這里是類似的,不同的是 ext 可以配置任何鍵值對(duì)的屬性值,而這里只能識(shí)別我們定義的 Java 類里的屬性值。
2.3 ExtensionContainer主要API功能及用法
2.3.1 創(chuàng)建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)
先來(lái)看看后面這個(gè) API 所有參數(shù)的含義。
- publicType:創(chuàng)建的 Extension 實(shí)例暴露出來(lái)的類類型;
- name:要?jiǎng)?chuàng)建的Extension的名字,可以是任意符合命名規(guī)則的字符串,不能與已有的重復(fù),否則會(huì)拋異常;
- instanceType:該Extension的類類型;
- constructionArguments:類的構(gòu)造函數(shù)參數(shù)值
官方文檔里還說(shuō)明了一個(gè)特性,創(chuàng)建的 Extension 對(duì)象都默認(rèn)實(shí)現(xiàn)了 ExtensionAware 接口,
The new instance will have been dynamically made ExtensionAware, which means that you can cast it to ExtensionAware.
我們來(lái)看一個(gè)具體的實(shí)例,包含了上面2個(gè) 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."
}
}
//創(chuàng)建的Extension是 Animal 類型
Animal aAnimal = getExtensions().create(Animal, "animal", Pig, 3, "hjy")
//創(chuàng)建的Extension是 Pig 類型
Pig aPig = getExtensions().create("pig", Pig, 5, "kobe")
animal {
legs = 4 //配置屬性
}
pig {
setLegs 2 //這個(gè)是方法調(diào)用,也就是 setLegs(2)
}
task testExt << {
println aAnimal
println aPig
//驗(yàn)證 aPig 對(duì)象是 ExtensionAware 類型的
println "aPig is a instance of ExtensionAware : ${aPig instanceof ExtensionAware}"
}
運(yùn)行 testExt 這個(gè)任務(wù),查看結(jié)果如下:
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() 方法會(huì)創(chuàng)建并返回一個(gè) Extension 對(duì)象,與之相似的還有一個(gè) add() 方法,唯一的差別是它并不會(huì)返回一個(gè) Extension 對(duì)象。
void add(Class<T> publicType, String name, T extension)
void add(String name, T extension)
基于前面的這個(gè)實(shí)例,我們可以換一種寫法如下:
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) //找不到會(huì)拋異常
<T> T getByType(Class<T> type) //找不到會(huì)拋異常
這幾個(gè) API 很好理解,一個(gè)是通過(guò)名字去查找,一個(gè)是通過(guò)類類型去查找。
2.4 嵌套Extension
類似下面這樣的配置應(yīng)該隨處可見(jiàn):
outer {
outerName "outer"
msg "this is a outer message."
inner {
innerName "inner"
msg "This is a inner message."
}
}
形式上就是外面的 Extension 里面定義了另一個(gè) Extension,這種叫做 nested Extension,也就是嵌套的 Extension。本文開(kāi)頭的 Android 打包配置,就是采用的這種方式。
那怎么創(chuàng)建上面這種 Extension 呢?
class OuterExt {
String outerName
String msg
InnerExt innerExt = new InnerExt()
void outerName(String name) {
outerName = name
}
void msg(String msg) {
this.msg = msg
}
//創(chuàng)建內(nèi)部Extension,名稱為方法名 inner
void inner(Action<InnerExt> action) {
action.execute(inner)
}
//創(chuàng)建內(nèi)部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
}
運(yùn)行結(jié)果如下:
OuterExt[ name = outer, msg = this is a outer message.] InnerExt[ name = inner, msg = This is a inner message.]
這里的關(guān)鍵點(diǎn)在于下面這2個(gè)方法的定義,只需要定義任意一個(gè)即可:
void inner(Action<InnerExt> action)
void inner(Closure c)
定義在 outer 內(nèi)部的 inner ,Gradle 解析時(shí)實(shí)質(zhì)上會(huì)進(jìn)行方法調(diào)用,也就是會(huì)執(zhí)行 outer.inner(...) 方法,而該方法的參數(shù)是一個(gè)閉包(俗稱 Script Block),所以在類 OuterExt 中必須定義 inner(...) 方法。
此外,前面說(shuō)到創(chuàng)建的 Extension 對(duì)象都是實(shí)現(xiàn)了 ExtensionAware 接口的,ExtensionAware 接口很簡(jiǎn)單,只包含一個(gè)方法:
ExtensionContainer getExtensions()
所以還有一種方式來(lái)創(chuàng)建嵌套的 Extension,只不過(guò)這種方式?jīng)]法自動(dòng)賦值到 OuterExt 類里的 innerExt 對(duì)象:
def innerExt = outExt.getExtensions().create("inner", InnerExt)
3. Android的Extension
先看個(gè) Android 的常規(guī)配置,以下是我的項(xiàng)目配置,截圖如下所示:

我們重點(diǎn)看看 defaultConfig、productFlavors、signingConfigs、buildTypes 這4個(gè)內(nèi)部 Extension對(duì)象是怎么定義的,通過(guò)查看源碼可以找到一個(gè)叫 BaseExtension 的類,里面的相關(guān)代碼如下:
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 是通過(guò)插件來(lái)創(chuàng)建的,關(guān)于插件的創(chuàng)建在我另一篇文章也有介紹:怎么創(chuàng)建Gradle插件。
在 app 的 build.gradle 里我們通常會(huì)采用插件 apply plugin: 'com.android.application' ,而在 library module 中則采用插件 apply plugin: 'com.android.library',先來(lái)看一張截圖:

圖中類 AppPlugin 就是插件 com.android.application 的實(shí)現(xiàn)類,LibraryPlugin 則是插件 com.android.library 的實(shí)現(xiàn)類,接著再看看 AppPlugin 里是怎樣創(chuàng)建 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() 方法中,可以看到創(chuàng)建了一個(gè)名為 android 的 Extension,該 Extension 的類型為 AppExtension,而 AppExtension 的繼承結(jié)構(gòu)為 AppExtension -> TestedExtension -> BaseExtension,所以它的實(shí)現(xiàn)邏輯大部分都是在 BaseExtension 里實(shí)現(xiàn)的。
LibraryExtension 的繼承結(jié)構(gòu)與 AppExtension 基本是一致的,有興趣的可以自己看源碼研究研究。
以后當(dāng)我們不知道 android 里有哪些配置時(shí),除了查看 API 文檔以外,還可以直接翻看 BaseExtension 源碼,基本上就能清楚了。
系列文章
Android Gradle學(xué)習(xí)(一):Gradle基礎(chǔ)入門
Android Gradle學(xué)習(xí)(二):如何創(chuàng)建Task
Android Gradle學(xué)習(xí)(三):Task進(jìn)階學(xué)習(xí)
Android Gradle學(xué)習(xí)(四):Project詳解
Android Gradle學(xué)習(xí)(五):Extension詳解
Android Gradle學(xué)習(xí)(六):NamedDomainObjectContainer詳解
Android Gradle學(xué)習(xí)(七):Gradle構(gòu)建生命周期
Android Gradle學(xué)習(xí)(八):統(tǒng)計(jì)Task執(zhí)行時(shí)長(zhǎng)
Android Gradle學(xué)習(xí)(九):一些有用的小技巧