寫給 Android 開發者的 Gradle 系列(一)基本姿式

歡迎關注本人公衆號,掃描下方二維碼或搜索公衆號 id: mxszgghtml

本文基於 Android Gradle plugin 3.0.1java

Gradle 介紹

筆者認爲可以戳進這篇文章的讀者十之八九也是知道 Gradle 能夠用來幹什麼,因此不必介紹什麼了,畢竟說一堆術語搞得你們都不懂很難堪(手動滑稽)。簡單來講,對於大部分的 Android 開發者來講 Gradle 是一個強大的工具,它提供便捷的方式幫助開發者構建 app。若是想看一下比較豐富的介紹的話能夠查看如何通俗地理解 Gradle?android

如何學習 Gradle

接下來筆者會出一系列關於 Gradle 文章,可是授人魚不如授人以漁 ——git

  • Gradle 基於 groovy 語言,groovy 官方文檔連接戳我。固然,對於相似筆者這種比較懶的程序員來講通常會選擇搜一些中文文章來看,如附錄中的Gradle從入門到實戰 - Groovy基礎。好消息是 groovy 與 java 相同是基於 jvm,因此理解起來並非那麼困難,且對於平常開發來講,真的不須要學習多少內容。程序員

  • Gradle DSL 學習。新建一個 Android 項目,能夠看到 project/build.gradle 文件中的內容相似以下:github

    buildscript {
    	repositories {
    	    jcenter()
    	}
    	dependencies {
    	    classpath 'com.android.tools.build:gradle:3.0.1'
    	}
    }
    	
    allprojects {
    	repositories {
    		jcenter()
    	}
    }
    
    task clean(type: Delete) {
        delete rootProject.buildDir
    }
    複製代碼

若是你想知道 buildscriptallprojects 的含義就應該戳開上面的連接了。例如文檔中說到 allprojects 是用於配置當前 project 和全部子 project 的,該方法將會在這些 project 中執行給定的閉包,那麼上述代碼閉包中的 repositories 的意義一樣是能夠在文檔中找到的。api

  • Android Plugin DSL 學習。新建一個 Android 項目,能夠看到 project/app/build.gradle 文件中的內容相似以下:bash

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 27
        defaultConfig {
            applicationId "com.joker.cliptest"
            minSdkVersion 21
            // ...
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.android.support:appcompat-v7:27.1.1'
        // ...
    }
    複製代碼

那麼像 buildTypes 的意義是什麼就能夠在上述文檔中找到了。閉包

  • Gradle task 學習。在前文提到的 project/build.gradle 文件中最後有一個app

    task clean(type: Delete) {
        delete rootProject.buildDir
    }
    複製代碼

其實就是一個 task,關於 task 的更多姿式在後一節中闡述。

前面共有四點,並且看起來每一點都須要花必定的時間去學習,其實否則,等到問題遇到了再去查文檔就行了,尤爲是前面提到的兩點 DSL 學習,其實平常開發中就已經可以理解其中的含義了,說白了它們其實就是一些閉包、一些固定格式,正是由於它們的格式是固定的,task 纔可以讀取到相應的數據完成相應的事情,因此各位讀者大沒必要懼怕 Gradle 學習成本高。

Gradle task

平常開發中,上一節提到的四點中,Gradle task 與大部分的開發者開發是最爲緊密的。平常開發中開發者不免會進行 build/clean project、build apk 等操做 ——

這裏寫圖片描述

實際上這些按鈕的底層實現都是經過 Gradle task 來完成的,只不過 IDE 使用 GUI 下降開發者們的使用門檻。固然,若是各位讀者足夠細心觀察的話,是可以看到點擊相應按鈕後在 Build 輸出欄中會輸出相應的信息 ——

這裏寫圖片描述

其實說到這裏各位讀者理應對 task 有一個基本的概念,一個 task 就是一個函數,沒有什麼神祕的地方。想要知道當前 Android 項目中共有哪些 task,能夠在項目目錄下輸入:

./gradlew tasks(Mac、Linux)

gradlew tasks(Windows,後文同)

將會輸出相似如下信息:

這裏寫圖片描述

這僅是部分 task,若是想要查看所有的 task 能夠添加 --all 參數:

./gradlew tasks --all

拉到最後的 Other task 部分:

這裏寫圖片描述

能夠看到全部的 task,可能有經驗的讀者更是感受到親切,提取部分 task 說明下:

  • compileDebugJavaWithJavac:編譯 java 文件
  • processDebugManifest:生成最終 AndroidManifest 文件
  • compileDebugAidl:編譯 AIDL 文件
  • packageDebug:打包成 apk

而若是爲 release 包開啓了混淆,可看到還有 transformClassesAndResourcesWithProguardForRelease task,即爲 release 包混淆。

這些 task 實際上貫穿了開發者們的平常開發流程,只是 IDE 在上層封裝了一層,開發者們點點按鈕就完成了這些操做。固然也有讀者會疑惑,這些 task 從哪裏來的?爲何沒有在 build.gradle 中看到蛛絲馬跡?這些 task 都是 plugin 中的,在 build.gradle 頂部 apply 的 plugin 中的(com.android.application/com.android.library)。在後續的文章中筆者將會介紹如何找到這些 task 的源碼、如何閱讀它們、如何寫 task、如何寫 plugin 以及解析較爲知名的 gradle 插件源碼,這些也正是 Android 開發者學習 gradle 的目的所在。

Gradle 構建週期

根據Build phases 文檔Settings file 文檔能夠知道,Gradle 構建週期分爲:

  • Initialization:Gradle支持單個和多項目構建。在 Initialization 階段,Gradle 將會肯定哪些項目將參與構建,併爲每一個項目建立一個 Project 對象實例。對於 Android 項目來講即爲執行 setting.gradle 文件。那麼平常開發中 setting.gradle 文件是如何書寫的呢,假設當前應用中除 app 之外還有一個 a module 和 b module,那麼 setting.gradle 文件應相似以下:

    include ':app', ':a', ':b'
    複製代碼

因此 Gradle 將會爲它們三個分別建立一個 Project 對象實例。

  • Configuration:在這一階段項目配置對象,全部項目的構建腳本將會被「執行」,這樣纔可以知道各個 task 之間的依賴關係。須要說的一點是,這裏提到的「執行」可能會稍有一些歧義:

    task a {
    
    }
    
    task testBoth {
    	// 依賴 a task 先執行
    	dependsOn("a")
    	println '我會在 Configuration 和 Execution 階段都會執行'
        doFirst {
          println '我僅會在 testBoth 的 Execution 階段執行'
        }
        doLast {
          println '我僅會在 testBoth 的 Execution 階段執行'
        }
    }
    複製代碼

    寫在 task 閉包中的內容是會在 Configuration 中就執行的,例如上面的 dependsOn("a")println 內容;而 doFirst {}/doLast {} 閉包中的內容是在 Execution 階段纔會執行到(doFirst {}/ doLast {} 實際上就是給當前 task 添加 Listener,這些 Listeners 只會在當前 task Execution 階段纔會執行)。

    建議各位讀者將這個 task 複製黏貼到 app 的 build.gradle 中,而後觀察點擊 Android Studio 的 Sync 按鈕(含 Configuration 階段)和在命令行輸入 ./gradlew app:testBoth 時命令行(testBothExecution 階段)輸出的結果。即可理解上述閉包內容的執行過程。固然你也能夠查看 Settings file 中的案例來理解。

    對於 Android 項目來講即爲執行各個 module 下的 build.gradle 文件,這樣各個 build.gradle 文件中的 task 的依賴關係就被確認下來了(例如 assembleDebug task 的執行依賴於其餘 tasks 先執行,而這個依賴關係的肯定就是在 Configuration 階段)。

  • Execution:task 的執行階段。首先執行 doFirst {} 閉包中的內容,最後執行 doLast {} 閉包中的內容。

在命令行中執行某一個 task,是能夠清晰地看見每個執行流程:

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

hook Gradle 構建過程

在命令行輸入 ./gradlew xxx 並按下回車以後的執行過程當中,就會執行上面的流程,再經過一張深刻理解Android之Gradle中的圖加深印象——

這裏寫圖片描述

然而在筆者目前爲止碰到的 Gradle 開發中,不多說起到上述的 hook 系列。用的比較多的一個是前文提到的 task 中的 doFirst {}/ doLast {},再一個就是 Project 在 Configuration 階段結束的 hook,前文也提到**Configuration 階段將會執行每個 Project 的 build.gradle 文件**,那麼如何監聽這每個 Project 的引入後的點呢?經過 Project.afterEvaluate ——

//afterEvaluate { Project project ->
//  println 'hook afterEvaluate'
//}

task hook { Project project ->
  afterEvaluate {
    println 'hook afterEvaluate'
  }
}
複製代碼

那麼爲何要 hook Project 的 afterEvaluate 階段呢?由於在 afterEvaluate 階段的時候,當前 Project 內的 task 信息才能被掌握,例如想在 assembleDebug task 前輸出一段信息,那麼在 app/build.gradle 中撰寫如下代碼:

// task badHook {
//   tasks.findByName("assembleDebug").doFirst {
//     println 'hook afterEvaluate from BadHook'
//   }
// }

task assDHook {
  afterEvaluate {
    tasks.findByName("assembleDebug").doFirst {
      println 'hook afterEvaluate from assHook'
    }
  }
}
複製代碼

此時若是調用 assembleDebug task 的話,首先是 Initialization 階段,再是 Configuration 階段,該階段中當 appProject 到達 afterEvaluate 階段的時候 appProject 中的 tasks 信息將會所有被獲取,固然這其中也包括 assembleDebug task,因此此時再給它添加一個 doFirst {} 閉包即可以達到目的。而像 badHook 閉包的內容將會在 Initialization 階段執行,此時 appProject 並無獲取所有的 task 信息,將會致使壓根找不到 assembleDebug 的錯誤。

後文

本文中並無總結太多的基礎知識,基本都之外鏈的形勢寫在了文中,由於筆者認爲如今已經有大量的文章寫的很不錯了,因此不想大費筆墨撰寫,筆者挑了一些自認爲較重要的點爲後文作鋪墊,另外,文中還有大量的外鏈是外鏈到官方文檔的,因此建議各位讀者在碰見問題的時候多多查閱文檔。各位讀者能夠在下文的附錄中閱讀更爲基礎的 Gradle 知識:

附錄

我建了一個羣,若是你對文章有什麼疑問,或者想和我討論 Android 技術,歡迎入羣哈。

相關文章
相關標籤/搜索