Android Gradle 入門到精通(一)

1.背景和意義

先后經(jīng)歷過(guò)多個(gè)Android開發(fā)團(tuán)隊(duì),每個(gè)團(tuán)隊(duì)都有這樣的一個(gè)人。他能在gradle中配置一些代碼。導(dǎo)致最終的apk 根據(jù)品牌+渠道 命名。甚至還能根據(jù)不同品牌,配置不同的變量。根據(jù)不同變量展示不同界面。gradle除了能幫我們做上述的事之外,還能做哪些呢?

  • hook Android 編譯過(guò)程
  • 配置和改善 Gradle 編譯速度

學(xué)習(xí)完gradle,對(duì)android 項(xiàng)目的構(gòu)建理解會(huì)更加深入;更加熟練的使用task,幫助我們構(gòu)建項(xiàng)目

2.概念

gralde它就是一個(gè)構(gòu)建工具,幫助我們編譯,打包,發(fā)布,檢查代碼格式,下載jar包等等。它是基于groovy語(yǔ)言編寫的。

問(wèn)題1: android如何自動(dòng)下載gradle
android 項(xiàng)目創(chuàng)建的時(shí)候,gradle文件中自帶gradle-wrapper.jar 這個(gè)jar包會(huì)根據(jù)gradle-wrapper.properties的配置 下載對(duì)應(yīng)的gradle 版本。

問(wèn)題2: ./gradlewgradle的區(qū)別

gradlew就是對(duì)gradle的包裝和配置,gradlew是gradle Wrapper,Wrapper的意思就是包裝。 因?yàn)椴皇敲總€(gè)人的電腦中都安裝了gradle,也不一定安裝的版本是要編譯項(xiàng)目需要的版本,那么gradlew里面就配置要需要的gradle版本

問(wèn)題3:什么是Project, 什么是Task,什么是Extension
android studio 每個(gè)module都有一個(gè)build.gradle文件,對(duì)于這個(gè)build.gradle而言,project 就是我們的module??梢酝ㄟ^(guò) gradle projects查看Project。

Extension 對(duì)我而言就是配置,初始化變量,這個(gè)變量的作用就是為Task提供參數(shù)。

Task 任務(wù),也是Action,表示這個(gè)任務(wù)是行為。例如assembleRelease是一個(gè)Task,用來(lái)執(zhí)行打包操作。打完的apk存放路徑這個(gè)路徑可以人為的配置。這個(gè)配置,在我看來(lái)就是拓展(Extension )

問(wèn)題4:什么是Gradle Daemon
Gradle Daemon是gralde的守護(hù)進(jìn)程。是一個(gè)長(zhǎng)時(shí)間運(yùn)行在后臺(tái)的進(jìn)程,避免頻繁的初始化。并且 Gradle 會(huì)將 Project data 緩存在內(nèi)存中,加快構(gòu)建速度。使用Gradle Daemon 是沒(méi)有任何副作用的,并且我們可以非常簡(jiǎn)單的使用它,不需要任何成本,Gradle Daomen 對(duì)我們用戶是完全透明的。可以通過(guò)gradle --stop停止

問(wèn)題4:什么是閉包Closure
Groovy中的閉包是一個(gè)開放,匿名的代碼塊,可以接受參數(shù),返回值并分配給變量。如:

//定義一個(gè)代碼塊
def innerClosure = {
    printf("hello world")
}
//執(zhí)行方式1
innerClosure()
//執(zhí)行方式2
innerClosure.call()

閉包也能傳遞參數(shù):

def innerClosure = {String s,Student student ->
    printf(s+student.toString())
}
innerClosure("hello world",new Student("chips","26"))

以上就是gralde的基本知識(shí)和概念

3.生命周期

gradle 的生命周期分為三個(gè)階段:

  • 初始化階段
  • 配置階段
  • 執(zhí)行階段

3.1 初始化階段

在初始化階段,Gradle的主要任務(wù)是確定有哪些工程參與構(gòu)建,會(huì)解析settings.gradle文件,為文件中參與構(gòu)建的的項(xiàng)目都創(chuàng)建project對(duì)象??梢酝ㄟ^(guò)gradle init完成

3.2 配置階段

在這個(gè)階段,Gradle會(huì)分別在每個(gè)Project對(duì)象上執(zhí)行對(duì)應(yīng)的build.gradle腳本,對(duì)Project進(jìn)行配置。gradle提供了倆個(gè)方法

void beforeEvaluate(Closure closure)  //Project 配置之前調(diào)用

void afterEvaluate(Closure closure)    //在 Project 配置結(jié)束后調(diào)用

其中需要注意的是:beforeEvaluate 必須在父模塊的 build.gradle 對(duì)子模塊進(jìn)行配置才能生效,因?yàn)樵诋?dāng)前模塊的 build.gradle 中配置,它自己本身都沒(méi)配置好,所以不會(huì)監(jiān)聽到。

3.3 執(zhí)行階段

在配置時(shí),Gradle 決定了在執(zhí)行階段要運(yùn)行的 task 的順序,他們的依賴關(guān)系的內(nèi)部結(jié)構(gòu)被建模為一個(gè)有向無(wú)環(huán)圖,我們可以稱之為 taks 執(zhí)行圖,它可以用 TaskExecutionGraph 來(lái)表示。可以通過(guò) gradle.taskGraph 來(lái)獲取。

在 TaskExecutionGraph 中也可以設(shè)置一些 Task 生命周期的回調(diào):

  • addTaskExecutionGraphListener(TaskExecutionGraphListener listener)
  • addTaskExecutionListener(TaskExecutionListener listener)
  • afterTask(Action action),afterTask(Closure closure)
  • beforeTask(Action action),beforeTask(Closure closure)
  • whenReady(Action action),whenReady(Closure closure)

4. 基礎(chǔ)使用

4.1 Task的基礎(chǔ)使用

task使用的官方文檔:官方文檔
build.gradle中task的定義:

task taskName(){
    //任務(wù)執(zhí)行前
    doFirst {
        println "doFirst"
    }
    //任務(wù)執(zhí)行完畢的打印
    doLast {
        println "doLast"
    }
}

dependsOn使用
task任務(wù)經(jīng)常存在相互依賴關(guān)系。A task 依賴于 Btask 可以借助dependsOn。在hook的時(shí)候用的比較多

task B {
    println "this is a task B"
}
task A(dependsOn: B) {
    println "this is a task A"
}

type使用
type用來(lái)表示創(chuàng)建的task 的類型

class MyTask extends DefaultTask {
    public String message
    public void test(String s) {
        println("this is MyTask " + message)
    }
}

task testDe(type: MyTask) {
    message = "chips"
    println test()
}

task之間存在順序性,使用到的api:mustRunAfter()shouldRunAfter()

task B {
    println "this is a task B"
}
task A(dependsOn: B) {
    println "this is a task A"
}

A.mustRunAfter(B)

這樣運(yùn)行A任務(wù)的話,也會(huì)順帶執(zhí)行B任務(wù)


自定義插件注冊(cè)Task的方式:

class MyPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        MyTask t = project.tasks.register("MyTask", MyTask).get()
        t.message="chips"
        println  t.toString()
    }
}

ps:千萬(wàn)別用project.getByName("MyTask")直接獲取task,gradle編譯出錯(cuò)

以上就是Task的基本用法,下面說(shuō)說(shuō)Extension的用法.

4.2 Extension 用法

什么是Extentsion?

就是 Gradle 的 Extension,翻譯成中文意思就叫擴(kuò)展。它的作用就是通過(guò)實(shí)現(xiàn)自定義的 Extension,可以在 Gradle 腳本中增加類似 android 這樣命名空間的配置,Gradle 可以識(shí)別這種配置,并讀取里面的配置內(nèi)容。
一般我們通過(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ì)象

在開始學(xué)習(xí)前,我們先看一個(gè)問(wèn)題:
android app的build.gradle中 有這樣的一段配置:

android {
    compileSdkVersion 31
    def versionCodeValue = 1
    def versionNameValue = "1.0"
}

這個(gè)android{} 就是一個(gè)名為android的Extension 。

我們來(lái)看看android這個(gè)Extension是如何定義的,系統(tǒng)源碼連接:AppPlugin

AppPlugin插件:

  @Override
    void apply(Project project) {
        super.apply(project)
     ....
        extension = project.extensions.create('android', AppExtension,
                this, (ProjectInternal) project, instantiator,
                buildTypeContainer, productFlavorContainer, signingConfigContainer)
}

注冊(cè)了一個(gè)名為android拓展,類型是AppExtension.

AppExtension:

public class AppExtension extends BaseExtension {
    final NamedDomainObjectContainer<DefaultProductFlavor> productFlavors
    final NamedDomainObjectContainer<DefaultBuildType> buildTypes
    final NamedDomainObjectContainer<SigningConfig> signingConfigs
    private final DefaultDomainObjectSet<ApplicationVariant> applicationVariantList =
        new DefaultDomainObjectSet<ApplicationVariant>(ApplicationVariant.class)
}

productFlavors/signingConfigs/buildTypes 分別是我們?cè)赽uild.gradle android下面配置的內(nèi)容。他們都是一個(gè)又一個(gè)的拓展。用來(lái)配置參數(shù),給Task的執(zhí)行提供參數(shù)。

AppExtension 還有哪些拓展,可以訪問(wèn)AppExtension官網(wǎng) 看看還有哪些拓展可以寫


使用Extension 分為三步:

  • 定義Extension 的參數(shù)類型
  • 在build.gradle中注冊(cè)這個(gè)Extension
  • 實(shí)例化拓展,且給每個(gè)元素賦值
  1. 定義一個(gè)Extension 類型
class MyExtension{
    String name
    String age
}

2.注冊(cè)Extension

getExtensions().create("MyExtension_拓展名稱", MyInfoExtension)

3.在build.gradle 使用:

myExtension {
    name = "wxy"
    age = "123"
}

如何訪問(wèn)myExtension 呢?

project.afterEvaluate {
    MyInfoExtension mMyInfoExtension = getExtensions().getByName("myExtension")
    println "Extension 使用:  "+mMyInfoExtension.name+"   "+mMyInfoExtension.age
}

以上就是Extension 基本應(yīng)用.

如何定義像android{}這樣的Extension呢?

class Address{
    public String address
}
class MyExtension {
    public String name
    public String age
    public Address addressInfor=new Address();
    // 配置方法
    void addressConfig(Action<Address> action) {
        action.execute(addressInfor) // 直接執(zhí)行 action 參數(shù)的 execute 方法,并傳入擴(kuò)展對(duì)象
    }
}

其中addressConfig 是必填項(xiàng).
這樣在build.gradle中就可以這樣定義了 :

getExtensions().create("myExtension", MyExtension)
myExtension {
    name = "chips"
    age = "123"
    addressConfig{
        address="湖北"
    }
}

我們還可以在.groovy文件中,調(diào)用:

class MyPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        project.getExtensions().create("MyInfoExtension",MyInfoExtension)
    }
}

這一步做完之后,相當(dāng)于我們注冊(cè)了這樣一個(gè)拓展。那么直接在buid.gradle中 進(jìn)行聲明就行.


4.3 屬性的訪問(wèn)

屬性的訪問(wèn)我遇到的有三種方式:

  • 根 build.gradle 中定義變量
  • 導(dǎo)入其他的.gradle,攜帶的變量
  • 訪問(wèn)gradle.properties
  • 訪問(wèn)local.properties
  1. 根build.gradle 定義的變量,其他的子build.gradle中都能訪問(wèn)。如:
project.ext {
    versionCode2=2021
}

project.ext 傳入一個(gè)閉包,返回一個(gè)Defaultextrapropertiesextension 拓展,此類實(shí)際上是用一個(gè)Map保存變量的。

  1. 導(dǎo)入其他的.gradle
    類似定義constants.gradle
project.ext {
    min_sdk_version     = 24
    compile_sdk_version = 'android-31'
    target_sdk_version  = 31
    build_tools_version = "30.0.3"
    java_Compatibility  = JavaVersion.VERSION_1_8 // 支持lambda
}

然后在build.gradle中
apply from: "${rootProject.projectDir}/constants.gradle"

3.gradle.properties 訪問(wèn)
在gradle.properties中定義各種變量 如:

versionCode5=2022

在build.gradle中就可以這樣使用:

defaultConfig {
     versionCode versionCode5  as int
}

必須得用as 轉(zhuǎn)換成對(duì)應(yīng)的類型,不然會(huì)報(bào)錯(cuò)

4.local.properties 訪問(wèn)
local元素的訪問(wèn)稍微麻煩點(diǎn).得new 一個(gè)Properties 對(duì)象,指定加載的文件

Properties properties = new Properties()
if (project.rootProject.file('local.properties').exists()) {
    properties.load(project.rootProject.file('local.properties').newDataInputStream())
}

---
properties.getProperty("versionCode3") as int

之后通過(guò)properties.getProperty("versionCode3") as int 得到值。響應(yīng)的也一定要加上as int,默認(rèn)是string類型

以上就是gradle的基礎(chǔ)知識(shí)。下一節(jié)講Gradle的自定義插件

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

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

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