Gradle系列分2章
上篇Android Gradle Groovy自動化構建入門篇
下篇Android Gradle Groovy自動化構建進階篇
上篇,我們已經介紹了Gradle的基本語法,接下來讓我們一起學習下Gradle高階知識:構建指令碼,自定義任務,構建生命週期,解決依賴衝突,多專案構建等高階技巧。
接下來,我們先看下gradle的幾個個簡單應用
- 由於專案越來越大,元件化盛行,有時我們不得不多module統一版本管理。一般做法是:
- 在 project 根目錄新建****.gradle 檔案;
- 通過
apply from
引入該配置檔案,然後使用rootProject.ext
引入相關屬性.
//1.比如這裡新建檔案為config.gradle
ext {
versions = [
sdkMinVersion : 14,
sdkTargetVersion : 26,
...
]
depVersion = [
appCompatVersion : "26.+",
recyclerViewVersion : "26.0.0-alpha1"
]
deps = [
suport : [
appcompat : "com.android.support:appcompat-v7:${depVersion.appCompatVersion}",
recyclerview: "com.android.support:recyclerview-v7:${depVersion.recyclerViewVersion}"
]
]
}
// 2. 引入已宣告好的屬性
apply from: 'config/config.gradle'
android {
def versions = rootProject.ext.versions
compileSdkVersion versions.sdkCompileVersion
buildToolsVersion versions.toolsBuildVersion
...
}
dependencies {
def dependencies = rootProject.ext.deps
compile dependencies.suport.appcompat
}
複製程式碼
- 在比如我們經常看到的這個錯誤:
Error:Execution failed for task ':test:processDebugManifest'.> Manifest merger failed with multiple errors, see logs
複製程式碼
我們一般的解決方案為:命令列輸入gradlew processDebugManifest --stacktrace
.
-
最後我們在看個簡單的單機執行任務
-
由於開發過程中經常匯入第三方jar包,一不小心就報jar包衝突這,這時我們會執行
gradle app:dependencies
檢視app重複依賴,然後在通過exclude剔除重複的jar。
compile ('com.android.support:design:22.2.1')
{
exclude group: 'com.android.support'
}
複製程式碼
其實當我們點選後Gradle會去尋找當前目錄下的 build.gradle 的檔案,這個檔案是 Gradle 的指令碼檔案,它裡面定義了工程和工程擁有的所有任務等資訊。然後執行相關task。下面我們一起來一步步揭開它的神祕面紗吧。
Gradle 中的工程( Project )和任務( Task )
就像上面的截圖一樣,我們知道,每一個 Gradle 的專案都會包含一個或多個工程,每一個工程又由一個或多個任務組成,一個任務代表了一個工作的最小單元,它可以是一次類的編譯、打一個 JAR 包、生成一份 Javadoc 或者是向倉庫中提交一次版本釋出。
任務的定義和使用
在任務中,我麼可以利用
dependsOn
定義依賴關係,doFirst
、doLast
對現有任務增強。 我們還是使用 IDEA 開發工具開啟之前的專案工程,把之前 build.gradle 檔案中所有的內容全部刪除,編寫輸入如下程式碼
task hello {
doLast {
println 'Hello world!'
}
}
task release() {
doLast {
println "I'm release task"
}
}
// 添任務依賴關係
release.dependsOn hello
//對現有的任務增強
// 法方一,在doFirst動作中新增
hello.doFirst {
println 'Hello doFirst'
}
// 法方二 在doLast動作中新增
hello.doLast {
println 'Hello doLast'
}
複製程式碼
開啟命令列端終,執行命令:gradle -q release
,輸出結果如下:
task myTask {
ext.myProperty = "myValue"
}
task printTaskProperties {
doLast {
println myTask.myProperty
}
}
命令列執行 ➜ gradle -q printTaskProperties
myValue
複製程式碼
當然我們可以對現有任務進行配置:禁用或者重寫。 首先我們定義一個 myCopy 的任務,程式碼如下:
task myCopy(type: Copy) {
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}
複製程式碼
類似java有API文件,Gradle也有類似文件,Gradle 中很多其它常用的任務,小夥伴們可以點選檢視, 如果我們想重寫 copy任務可通過overwrite屬性為 true 來現如下所示:
task copy(type: Copy)
task copy(overwrite: true) {
doLast {
println('overwrite the copy.')
}
}
命令列執行
> gradle -q copy
overwrite the copy.
複製程式碼
最後我們看下如何禁用某些任務。直接看程式碼吧
copy.enabled = false
複製程式碼
實戰篇
接下來我們來自定義外掛,有三種方式來編寫
-
在我們構建專案的 build.gradle 指令碼中直接編寫,這種方式的好處是外掛會自動被編譯載入到我們的 classpath 中,但是它有很明顯的侷限性,就是除了在包括它的指令碼外別的地方無法複用。
-
在我們構建專案的rootProjectDir/buildSrc/src/main/groovy 目錄下編寫,Gradle 會自動編譯到當前專案的 classpath 中,該專案下所有編譯指令碼都可以使用,但是除了當前專案之外的都無法複用。
-
以單獨的工程方式編寫,這個工程最終編譯釋出為一個 JAR 包,它可以在多個專案或不同的團隊中共享使用。
接下來我們一步步把按照第三種方式寫個Demo吧。
首先使用IDEA新建gradle 工程選擇groovy(跟上面一樣就不細說了),然後按照下面截圖新建src/main/groovy/你的包名,接著在resources 目錄下建立 META-INF/gradle-plugins 資料夾,在其中新建 hello.properties 檔案,敲黑板注意此處檔名,就是以後使用時要用的名字。此處是hello,所以我們得按照這樣引入apply plugin: 'hello'
,裡面像這樣輸入外掛的全路徑名字:implementation-class=org.gradle.HelloPlugin
接下來分 2 步編寫程式碼:
- 繼承自DefaultTask的,使用TaskAction進行標註,這樣 Gradle 就會在任務執行的時候預設呼叫它
- 然後通過實現Plugin介面來實現自定義外掛類,實現apply(Project project) 方法。
按照步驟,首先我們新建MyTask.groovy檔案,裡面僅僅是簡單宣告瞭一個成員變數,然後列印。
class MyTask extends DefaultTask {
String input = 'hello from MyTask'
@TaskAction
def greet() {
println input
}
}
複製程式碼
然後我們新建Hello.groovy檔案.我們向這個plugin新增了一個hello任務
,我們知道gradle中可以配置引數比如:defaultConfig {} ndk {}
等,其實gradle是使用 extension objects來現實給外掛傳參,具體實現看下面程式碼的註釋:
class Hello implements Plugin<Project> {
@Override
void apply(Project project) {
// 向extension container儲存para引數,並應用給HelloPluginExtension
project.extensions.create("para", HelloPluginExtension)
// 向project物件新增hello任務
project.task('hello',type:MyTask) {
input = 'Hello Plugin input!'
doLast {
println "${project.para.first}${project.para.last}"
}
}
}
}
複製程式碼
接下來發布工程到本地倉庫,供其他專案使用,在build.gradle
中輸入
//使用 maven-publish 外掛先發布到本地
apply plugin: 'maven-publish'
publishing{
publications {
mavenJava(MavenPublication) {
from components.java
groupId 'org.gradle'
artifactId 'customPlugin'
version '1.0-SNAPSHOT'
}
}
repositories{
maven {
// change to point to your repo, e.g. http://my.org/repo
url "../repo"
}
}
}
複製程式碼
點選publish任務,看到成功釋出工程到本地倉庫../repo中了。
最後用 IDEA 開發工具新建立一個 Gradle 工程來驗證我們的外掛。看到右側gradle任務中多了我們新增的hello任務,點選檢視成功輸出了Hello world。補充及常見問題
檔案樹是有層級結構的檔案集合,一個檔案樹它可以代表一個目錄結構或一 ZIP 壓縮包中的內容結構。使用Project.fileTree(java.util.Map)
建立,可以使用過慮條件來包含或排除相關檔案。
// 指定目錄建立檔案樹物件
FileTree tree = fileTree(dir: 'src/main')
// 給檔案樹物件新增包含指定檔案
tree.include '**/*.java'
//andoid中使用檔案樹
implementation fileTree(include: ['*.jar'], dir: 'libs')
複製程式碼
多module的編譯配置
注意 settings.gradle
引入的module才會參與編譯include ':app', ':plugin_common', ':plugin_gallery'
,可以在跟guild.gradle
中統一設定公共行為;比如下圖新增一個hello任務。
一個工程的路徑為:以冒號(: 它代表了根工程)開始,再加上工程的名稱。例如“:common”。
一個任務的路徑為:工程路徑加上任務名稱,例如“:common:hello”.
比如:僅僅執行 gradle :plugin_common:hello