Gradle進階計劃(一)Gradle初探

        相信對於任何一位安卓開發工程師來說,Gradle一定不陌生。但是很多人甚至是很多年開發經驗的高級工程師對Gradle仍然不甚瞭解,市面上涉及Gradle的文章大多數講解的也不是很系統。本專欄旨在系統的梳理下Gradle的相關知識點,希望能對你有所幫助。

 

一、什麼是 Gradle

“Gradle is an open-source build automation system that builds upon the concepts of Apache Ant and Apache Maven and introduces a Groovy-based domain-specific language (DSL) instead of the XML form used by Apache Maven for declaring the project configuration.[1] Gradle uses a directed acyclic graph ("DAG") to determine the order in which tasks can be run.”——維基百科對Gradle的定義。

         翻譯過來就是:“Gradle是一個基於Apache Ant和Apache Maven概念的項目自動化構建開源工具。它使用一種基於Groovy的特定領域語言(DSL)來聲明項目設置,拋棄了基於XML的各種繁瑣配置。”

        可以從三個角度來理解:

1. Gradle 是一個自動化構建工具

        Gradle 是通過組織一系列 Task 來最終完成自動化構建的,以生成一個可用的 apk 爲例,整個過程要經過 資源的處理,javac 編譯,dex 打包,apk 打包,簽名等步驟,每個步驟就對應到 Gradle 裏的一個 Task。

2. 編寫 Gradle 腳本可以使用 Groovy 或者 Kotlin DSL

        Gradle 腳本 使用 Groovy 或者 Kotlin 編寫,不過目前還是 Groovy 居多。DSL 也就是 Domain Specific Language 的簡稱,是爲了解決某一類任務專門設計的計算機語言。與 GPL(General-Purpose Language,比如 Java) 相比起來,DSL 使用簡單,定義比較簡潔,比起配置文件,DSL 又可以實現語言邏輯。對 Gradle 腳本來說,他實現了簡潔的定義,又有充分的語言邏輯,以 android {} 爲例,這本身是一個函數調用,參數是一個閉包,但是這種定義方式明顯要簡潔很多。

3. Gradle 基於 Groovy 編寫,而 Groovy 是基於 JVM 語言

        Gradle 使用 Groovy 編寫,Groovy 是基於 JVM 的語言,所以本質上是面向對象的語言,面嚮對象語言的特點就是一切皆對象,所以,在 gradle 裏,.gradle 腳本的本質就是類的定義一些配置項的本質都是方法調用,參數是後面的 {} 閉包。比如 build.gradle 對應 Project 類,buildScript 對應 Project.buildScript 方法

 

二、Gradle 項目層次

(一)setting.gradle

        settings.gradle 是負責配置項目的腳本。對應 Settings 類,Gradle 構建過程中,會根據 settings.gradle 生成 Settings 的對象。

        其中幾個主要的方法有:

  • include(projectPaths)  //子模塊引用
  • include(projectDir)
  • includeFlat(projectNames)  //平級引用
include ':app'
//指定子模塊的位置,使用 project 方法獲取 Project 對象,設置其 projectDir 參數
project(':app').projectDir = new File('./app')

(二)rootproject/build.gradle

        build.gradle 負責整體項目的一些配置,對應的是 Project 類,gradle 構建的時候,會根據 build.gradle 生成 Project 對象。Project 其實是一個接口,真正的實現類是 DefaultProject

        其中幾個主要方法有:

  • buildscript // 配置腳本的 classpath
  • allprojects // 配置項目及其子項目
  • respositories // 配置倉庫地址,後面的依賴都會去這裏配置的地址查找
  • dependencies // 配置項目的依賴
buildscript { // 配置項目的 classpath
    repositories {  // 項目的倉庫地址,會按順序依次查找
        google()
        jcenter()
        mavenLocal()
    }
    dependencies { // 項目的依賴
        classpath 'com.android.tools.build:gradle:3.0.1'
        classpath 'com.zy.plugin:myplugin:0.0.1'
    }
}

allprojects { // 子項目的配置
    repositories {
        google()
        jcenter()
        mavenLocal()
    }
}

(三)module/build.gradle

        build.gradle 是子項目的配置,對應的也是 Project 類
        子項目和根項目的配置是差不多的,不過在子項目裏可以看到有一個明顯的區別,就是引用了一個插件 apply plugin "com.android.application",後面的 android dsl 就是 application 插件的 extension。關於 android plugin dsl 可以看 android-gradle-dsl(需要科學上網)。
        其中幾個主要方法有:

  • compileSdkVersion // 指定編譯需要的 sdk 版本
  • defaultConfig // 指定默認的屬性,會運用到所有的 variants(構建變體)
  • buildTypes // 配置編譯屬性
  • productFlavor // 配置項目的 flavor
apply plugin: 'com.android.application' // 引入 android gradle 插件

android { // 配置 android gradle plugin 需要的內容
    compileSdkVersion 26
    defaultConfig { // 版本,applicationId 等配置
        applicationId "com.zy.easygradle"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }
    buildTypes { 
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions { // 指定 java 版本
        sourceCompatibility 1.8
        targetCompatibility 1.8
    }

    // flavor 相關配置
    flavorDimensions "size", "color"
    productFlavors {
        big {
            dimension "size"
        }
        small {
            dimension "size"
        }
        blue {
            dimension "color"
        }
        red {
            dimension "color"
        }
    }
}

// 項目需要的依賴
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar']) // jar 包依賴
    implementation 'com.android.support:appcompat-v7:26.1.0' // 遠程倉庫依賴
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation project(':module1') // 項目依賴
}

 (四)gradle wrapper

        gradlew / gradlew.bat 這個文件用來下載特定版本的 gradle 然後執行的,就不需要開發者在本地再安裝 gradle 了。

        這樣做有什麼好處呢?開發者在本地安裝 gradle,會碰到的問題是不同項目使用不同版本的 gradle 怎麼處理,用 wrapper 就很好的解決了這個問題。gradle wrapper 一般下載在 GRADLE_CACHE/wrapper/dists 目錄下。

        gradle/wrapper/gradle-wrapper.properties 是一些 gradlewrapper 的配置,其中用的比較多的就是 distributionUrl,可以執行 gradle 的下載地址和版本。
        gradle/wrapper/gradle-wrapper.jar 是 gradlewrapper 運行需要的依賴包。

(五)gradle init

        在 gradle 裏,有一種 init.gradle 比較特殊,這種腳本會在每個項目 build 之前先被調用,可以在其中做一些整體的初始化操作,比如配置 log 輸出等。
        使用 init.gradle 的方法:

  1. 通過 --init-script 指定 init.gradle 位置 eg: gradlew --init-script initdir/init.gradle
  2. init.gradle 文件放在 USER_HOME/.gradle/ 目錄下
  3. .gradle 腳本放在 USER_HOME/.gradle/init.d/ 和 GRDALE_HOME/init.d/ 目錄下

 

三、Gradle 生命週期

        Gradle 構建分爲三個階段:
        1. 初始化階段
        初始化階段主要做的事情是解析 settings.gradle 來獲取模塊信息,明確有哪些項目需要被構建,然後爲對應的項目創建 Project 對象。

        2. 配置階段
        配置階段主要做的事情是對上一步創建的項目進行配置,這時候會執行 build.gradle 腳本,並且會生成要執行的 Task。

        3. 執行階段
        執行階段主要做的事情就是執行 Task,進行主要的構建工作。

 

四、Gradle 依賴

        在 gradle 3.4 裏引入了新的依賴配置,如下:

新配置 棄用配置 行爲 作用
implementation compile 依賴項在編譯時對模塊可用,並且僅在運行時對模塊的消費者可用。 對於大型多項目構建,使用 implementation 而不是 api/compile 可以顯著縮短構建時間,因爲它可以減少構建系統需要重新編譯的項目量。 大多數應用和測試模塊都應使用此配置。 implementation 只會暴露給直接依賴的模塊,使用此配置,在模塊修改以後,只會重新編譯直接依賴的模塊,間接依賴的模塊不需要改動
api compile 依賴項在編譯時對模塊可用,並且在編譯時和運行時還對模塊的消費者可用。 此配置的行爲類似於 compile(現在已棄用),一般情況下,您應當僅在庫模塊中使用它。 應用模塊應使用 implementation,除非您想要將其 API 公開給單獨的測試模塊。 api 會暴露給間接依賴的模塊,使用此配置,在模塊修改以後,模塊的直接依賴和間接依賴的模塊都需要重新編譯
compileOnly provided 依賴項僅在編譯時對模塊可用,並且在編譯或運行時對其消費者不可用。 此配置的行爲類似於 provided(現在已棄用)。 只在編譯期間依賴模塊,打包以後運行時不會依賴,可以用來解決一些庫衝突的問題
runtimeOnly apk 依賴項僅在運行時對模塊及其消費者可用。 此配置的行爲類似於 apk(現在已棄用)。 只在運行時依賴模塊,編譯時不依賴

        現在假設項目裏有三個模塊:app,module1, module2

  •         模塊 app 中有一個類 ModuleApi
  •         模塊 module1 中有一個類 Module1Api
  •         模塊 module2 中有一個類 Module2Api

        其依賴關係如下:

1. implementation 依賴
        當 module1 使用 implementation 依賴 module2 時,在 app 模塊中無法引用到 Module2Api 類。

2. api 依賴
        當 module1 使用 api 依賴 module2 時,在 app 模塊中可以正常引用到 Module2Api 類。

3. compileOnly 依賴
        當 module1 使用 compileOnly 依賴 module2 時,在編譯階段 app 模塊無法引用到 Module2Api 類,module1 中正常引用,但是在運行時會報錯。

        反編譯打包好的 apk,可以看到 Module2Api 是沒有被打包到 apk 裏的。

        

4. runtimeOnly 依賴
        當 module1 使用 runtimeOnly 依賴 module2 時,在編譯階段,module1 也無法引用到 Module2Api。

 

五、Gradle Flavor

        Flavor是在Gradle中配置多渠道的打包的工具,它將 debug 和 release 維度進一步擴大。

        在 android gradle plugin 3.x 之後,每個 flavor 必須對應一個 dimension,可以理解爲 flavor 的分組,然後不同 dimension 裏的 flavor 兩兩組合形成一個 variant

flavorDimensions "size", "color"

productFlavors {
    big {
        dimension "size"
    }
    small {
        dimension "size"
    }
    blue {
        dimension "color"
    }
    red {
        dimension "color"
    }
}

        那麼生成的 variant 對應的就是 bigBlue,bigRed,smallBlue,smallRed。
        每個 variant 可以對應的使用 variantImplementation 來引入特定的依賴,比如:bigBlueImplementation,只有在 編譯 bigBlue variant的時候纔會引入。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章