第一次看到 NamedDomainObjectContainer 的時候,讓我迷惑了好一陣子,這到底是個什麼玩意?現在讓我們來揭開它神秘的面紗,看看它到底是什麼,有什麼作用。
1. NamedDomainObjectContainer的使用場景
前面在講解 Gradle Extension 的時候,說到名為 android 的 Extension 是由 BaseExtension 這個類來實作的,裡面對 buildTypes 是這樣定義的:
buildTypes 就是 NamedDomainObjectContainer 類型的,先來看看 buildTypes 在 Android 中是怎麼使用的,下面這段代碼應該都很熟悉了,它定義了 debug、relase 兩種打包模式:
android {
buildTypes {
release {
// 是否開啟混淆
minifyEnabled true
// 開啟ZipAlign優化
zipAlignEnabled true
//去掉不用資源
shrinkResources true
// 混淆檔案位置
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// 使用release簽名
signingConfig signingConfigs.hmiou
}
debug {
signingConfig signingConfigs.hmiou
}
}
}
當我們建立一個項目的時候,預設會有 debug、release 這2個配置,那麼問題來了:debug、release 能不能修改為其他名字?能不能增加其他的名字來配置,比方說我想增加一個測試包配置 test ?還有就是 release 裡面都能配置哪些屬性呢?
我來說下結果,如果不确定的,可以實際驗證一下:
- debug、release 是可以修改成其他名字的,你可以替換成你喜歡的名字;
- 你可以增加任意不同名字的配置,比如增加一個開發版本的打包配置 dev ;
- 可配置的屬性可參考接口:com.android.builder.model.BuildType ;
可以看到它是非常靈活的,你可以根據不同的場景定義不同的配置,每個不同的命名空間都會生成一個 BuildType 配置。要實作這樣的功能,必須使用 NamedDomainObjectContainer 類型。
2. NamedDomainObjectContainer是什麼
顧名思義就是命名領域對象容器,它的主要功能有:
- 它能夠通過DSL(在Gradle腳本中)建立指定 type 的對象執行個體;
- 指定的 type 必須有一個 public 構造函數,且必須帶有一個 String name 的參數,type 類型的領域對象必須有名為“name”的屬性;
- 它是一個實作了 SortedSet 接口的容器,是以所有領域對象的 name 屬性值都必須是唯一的,在容器内部會用 name 屬性來排序;
來看看官方文檔裡的說明:
A named domain object container is a specialisation of NamedDomainObjectSet that adds the ability to create instances of the element type.
Note that a container is an implementation of SortedSet, which means that the container is guaranteed to only contain elements with unique names within this container. Furthermore, items are ordered by their name.
3. 怎麼建立NamedDomainObjectContainer
NamedDomainObjectContainer 需要通過 Project.container(…) API 來建立,其定義為:
<T> NamedDomainObjectContainer<T> container(Class<T> type)
<T> NamedDomainObjectContainer<T> container(Class<T> type, NamedDomainObjectFactory<T> factory)
<T> NamedDomainObjectContainer<T> container(java.lang.Class<T> type, Closure factoryClosure
來看個具體的執行個體:
//這是領域對象類型定義
class TestDomainObj {
//必須定義一個 name 屬性,并且這個屬性值初始化以後不要修改
String name
String msg
//構造函數必須有一個 name 參數
public TestDomainObj(String name) {
this.name = name
}
void msg(String msg) {
this.msg = msg
}
String toString() {
return "name = ${name}, msg = ${msg}"
}
}
//建立一個擴充
class TestExtension {
//定義一個 NamedDomainObjectContainer 屬性
NamedDomainObjectContainer<TestDomainObj> testDomains
public TestExtension(Project project) {
//通過 project.container(...) 方法建立 NamedDomainObjectContainer
NamedDomainObjectContainer<TestDomainObj> domainObjs = project.container(TestDomainObj)
testDomains = domainObjs
}
//讓其支援 Gradle DSL 文法
void testDomain(Action<NamedDomainObjectContainer<TestDomainObj>> action) {
action.execute(testDomains)
}
void test() {
//周遊命名領域對象容器,列印出所有的領域對象值
testDomains.all { data ->
println data
}
}
}
//建立一個名為 test 的 Extension
def testExt = getExtensions().create("test", TestExtension, project)
test {
testDomain {
domain2 {
msg "This is domain2"
}
domain1 {
msg "This is domain1"
}
domain3 {
msg "This is domain3"
}
}
}
task myTask << {
testExt.test()
}
運作結果如下:
name = domain1, msg = This is domain1
name = domain2, msg = This is domain2
name = domain3, msg = This is domain3
4. 查找和周遊
NamedDomainObjectContainer 既然是一個容器類,與之相應的必然會有查找容器裡的元素和周遊容器的方法:
//周遊
void all(Closure action)
//查找
<T> T getByName(String name)
//查找
<T> T findByName(String name)
還是接着前面的例子:
//通過名字查找
TestDomainObj testData = testDomains.getByName("domain2")
println "getByName: ${testData}"
//周遊命名領域對象容器,列印出所有的領域對象值
testDomains.all { data ->
println data
}
需要注意的是,Gradle 中有很多容器類的疊代周遊方法有 each(Closure action)、all(Closure action),但是一般我們都會用 all(…) 來進行容器的疊代。all(…) 疊代方法的特别之處是,不管是容器内已存在的元素,還是後續任何時刻加進去的元素,都會進行周遊。
相關文章
Android Gradle學習(一) Gradle基礎入門
Android Gradle學習(二) 如何建立Task
Android Gradle學習(三) Task進階學習
Android Gradle學習(四) Project詳解
Android Gradle學習(五) Extension詳解
Android Gradle學習(六) NamedDomainObjectContainer詳解
Android Gradle學習(七) Gradle建構生命周期
Android Gradle學習(八) 統計Task執行時長