Android Gradle學(xué)習(xí)(五):Extension詳解

前面我們已經(jīng)詳細(xì)講解了 Gradle 的 Task、Project 等基本用法,現(xiàn)在我們還要學(xué)習(xí)一個(gè)很重要的概念 Extension,它在 Gradle 中幾乎隨處可見,特別是在 Android 打包配置中。

1. 什么是Extension

我們先來看一段 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)用開發(fā)的同學(xué),對這段代碼都快看吐了吧。記得當(dāng)初剛從 eclipse 轉(zhuǎn)到 Android Studio 的時(shí)候,看這些配置就像看天書一樣,只知道按規(guī)定配置就可以了。但是為什么要這樣配置?除此外還支持哪些配置?為什么一定要在 android 這個(gè)命名空間下配置呢?可以不可以定義自己的特殊配置呢?

上面這個(gè) android 打包配置,就是 Gradle 的 Extension,翻譯成中文意思就叫擴(kuò)展。它的作用就是通過實(shí)現(xiàn)自定義的 Extension,可以在 Gradle 腳本中增加類似 android 這樣命名空間的配置,Gradle 可以識別這種配置,并讀取里面的配置內(nèi)容。

2. 怎么定義Extension

2.1 ExtensionContainer

一般我們通過 ExtensionContainer 來創(chuàng)建 Extension,這個(gè)類與 TaskContainer 命名有點(diǎn)類似,TaskContainer 是用來創(chuàng)建并管理 Task 的,而 ExtensionContainer 則是用來創(chuàng)建并管理 Extension 的。通過 Project 的以下 API 可以獲取到 ExtensionContainer 對象:

ExtensionContainer getExtensions()
2.2 簡單的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 << {
    //能直接通過 project 獲取到自定義的 Extension
    println project.foo
}

上面這個(gè)例子中,foo 就是我們自定義的 Extension 了,它里面能配置的屬性與類 Foo 中的字段是一致的,在 build.gradle 中可以直接通過 project.foo 來訪問。每個(gè) Extension 實(shí)際上與某個(gè)類是相關(guān)聯(lián)的,在 build.gradle 中通過 DSL 來定義,Gradle 會識別解析并生成一個(gè)對象實(shí)例,通過該類可以獲取我們所配置的信息。

之前有講過 Project 有個(gè)擴(kuò)展屬性是通過 ext 命名空間配置的,可以看到 ext 與這里是類似的,不同的是 ext 可以配置任何鍵值對的屬性值,而這里只能識別我們定義的 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)

先來看看后面這個(gè) API 所有參數(shù)的含義。

  • publicType:創(chuàng)建的 Extension 實(shí)例暴露出來的類類型;
  • name:要?jiǎng)?chuàng)建的Extension的名字,可以是任意符合命名規(guī)則的字符串,不能與已有的重復(fù),否則會拋異常;
  • instanceType:該Extension的類類型;
  • constructionArguments:類的構(gòu)造函數(shù)參數(shù)值

官方文檔里還說明了一個(gè)特性,創(chuàng)建的 Extension 對象都默認(rèn)實(shí)現(xiàn)了 ExtensionAware 接口,

The new instance will have been dynamically made ExtensionAware, which means that you can cast it to ExtensionAware.

我們來看一個(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 對象是 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() 方法會創(chuàng)建并返回一個(gè) Extension 對象,與之相似的還有一個(gè) add() 方法,唯一的差別是它并不會返回一個(gè) Extension 對象。

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)       //找不到會拋異常
<T> T getByType(Class<T> type)  //找不到會拋異常

這幾個(gè) API 很好理解,一個(gè)是通過名字去查找,一個(gè)是通過類類型去查找。

2.4 嵌套Extension

類似下面這樣的配置應(yīng)該隨處可見:

outer {
    
    outerName "outer"
    msg "this is a outer message."

    inner {
        innerName "inner"
        msg "This is a inner message."
    }
    
}

形式上就是外面的 Extension 里面定義了另一個(gè) Extension,這種叫做 nested Extension,也就是嵌套的 Extension。本文開頭的 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ì)上會進(jìn)行方法調(diào)用,也就是會執(zhí)行 outer.inner(...) 方法,而該方法的參數(shù)是一個(gè)閉包(俗稱 Script Block),所以在類 OuterExt 中必須定義 inner(...) 方法。

此外,前面說到創(chuàng)建的 Extension 對象都是實(shí)現(xiàn)了 ExtensionAware 接口的,ExtensionAware 接口很簡單,只包含一個(gè)方法:

ExtensionContainer getExtensions()

所以還有一種方式來創(chuàng)建嵌套的 Extension,只不過這種方式?jīng)]法自動賦值到 OuterExt 類里的 innerExt 對象:

def innerExt = outExt.getExtensions().create("inner", InnerExt)

3. Android的Extension

先看個(gè) Android 的常規(guī)配置,以下是我的項(xiàng)目配置,截圖如下所示:

Android 配置

我們重點(diǎn)看看 defaultConfig、productFlavors、signingConfigs、buildTypes 這4個(gè)內(nèi)部 Extension對象是怎么定義的,通過查看源碼可以找到一個(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 是通過插件來創(chuàng)建的,關(guān)于插件的創(chuàng)建在我另一篇文章也有介紹:怎么創(chuàng)建Gradle插件。

在 app 的 build.gradle 里我們通常會采用插件 apply plugin: 'com.android.application' ,而在 library module 中則采用插件 apply plugin: 'com.android.library',先來看一張截圖:

plugin.jpg

圖中類 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í)長
Android Gradle學(xué)習(xí)(九):一些有用的小技巧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • afinalAfinal是一個(gè)android的ioc,orm框架 https://github.com/yangf...
    wgl0419閱讀 6,602評論 1 9
  • 1、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,228評論 3 119
  • 人與人的差距根本不在智商,而是在思維的高度上。整天糾纏于雞毛蒜皮,就會忽略那些大的人生命題。決定人生上限的,往往不...
    東方新材粉末彩涂板閱讀 281評論 0 0
  • 讀了好多雞湯文說掙錢得重要性,有錢得重要性,大多說得非?,F(xiàn)實(shí), 無非未來和意外不知道哪一個(gè)先來,無非有...
    靜水凌波閱讀 210評論 0 0
  • 阿菱:這多大的海灘啊,怎么沒有人?。?阿球:我們不是來了嗎?你是在緬懷夏日的海灘吧? 阿菱:對,夏日的海灘,嘈雜喧...
    云水坡頭閱讀 572評論 0 0

友情鏈接更多精彩內(nèi)容