作爲一個Android開發人員,相信大家每天開發都在與Gradle打交道,它是一款非常優秀的構建工具。基於Groovy語言實現,但是又與java完全兼容,在編寫Gradle腳本的時候,完全可以按照java的語法去寫相關的類,變量,方法。
Gradle 項目構建生命週期
Gradle 的生命週期分三個階段,初始化階段,配置階段,執行階段.那這三個階段在做什麼事情呢?請看每個階段的描述
初始化階段
通過 settings.gradle 判斷有哪些項目需要初始化,加載所有需要初始化的項目的build.gradle 文件併爲每個項目創建 project 對象。setting裏面的配置先初始化,然後是各個module中的build.gradle執行...
配置階段
執行各項目下的 build.gradle 腳本,完成 project 的配置,並且構造 Task 任務依賴關係圖以便在執行階段按照依賴關係執行Task.執行task 中的配置代碼,正如下面定義的task hello ,定義task時候配置的閉包(類似java的構造函數)。
task hello{
println('這是配置階段執行的代碼')
}
執行階段
通過配置階段的 Task 依賴關係圖,按順序執行需要執行的 任務中的動作代碼,就是執行任務中寫在action, doFirst 或 doLast 中的代碼.在AndroidStudio中新建一個Android項目,在項目的根目錄下會自動幫我們生成一個build.gradle文件,文件內容如下:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
//相關插件的引用的地方
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
這個build.gralde是AndroidStudio自動我們生成的,這裏面配置了repositories ,這個裏面其實配置的是gradle的倉庫,3.X.X之後,需要配置google(),AndroidStuido 會去相應的倉庫去下載我們的插件。dependencies裏面配置我們的插件,gradle插件是系統幫我們配置好的,只有配置好這個,在app module下的gradle才能使用application插件
apply plugin: 'com.android.application'
在這裏我們也可以配置自己自定義的插件以及項目中引用的第三方庫的插件。
這裏面定義了一個task任務,名稱爲clean,type爲Delete。當我們將光標定位到task clean這裏的時候,task 是可以點進去的,類似於java當中的方法實現,點進去之後,我們可以看到,它其實是project中的一個方法(其實上面的build.gradle文件下的根節點 buildScript也是project當中的一個方法),這種定義的方法使得,我們可以在build中對其進行配置,然後gradle插件自動去解析生成對應的“閉包”,得到相關的屬性,方法,任務。
那我們其實可以猜測一下,這個task clean任務 有一個參數,type其實就是我們構造task可以傳遞的一些參數:
可以看到默認的參數Task中默認的幾個參數
配置項 | 描述 | 默認值 |
---|---|---|
name | 任務名稱 | task 後面的緊跟的名稱,不知道這個配置的作用 |
group | 用於配置任務的分組 | null |
type | 基於一個存在的task來創建,和累的繼承相似 | DefaultTask |
dependsOn | 配置任務的依賴,描述一個任務基於另外一個任務執行 | [ ] |
action | 添加到任務中的一個action或者閉包 | null |
description | 配置任務的描述 | null |
overwrite | 是否替換存在的task,這個和type配合來使用 |
false |
task clean(type: Delete) {
delete rootProject.buildDir
}
配置clean 任務的時候,或直接執行裏面的代碼,我們可以配置兩個閉包,在執行task任務之前,doFrist執行一段代碼,在執行task任務之後執行doLast裏面的閉包。
自定義Task
我們在編譯運行Android項目的時候,都會看到gradle爲我們配置了許多運行Android項目需要執行的任務,這個在編譯的時候,AndroidStudio底部的build欄可以看到:
例如我們在app module下的build.gradle中,定義一個任務hello:
task hello(group:'hellogroup',action:{
println('========= hello任務正在執行ing ===============') } ){
println('========= hello任務配置中 ===============')
doFirst {
println('========= hello任務執行之前 ===============')
}
doLast {
println('========= hello任務執行之後 ===============')
}
}
此時,我們會在右側的gradle欄裏面的other裏面看到生成了一個gradle的任務,名稱爲hello,我們點擊這個hello之後,在控制檯就可以看到下面的輸出:
可以看出action中配置的閉包,纔是我們任務真正執行的時候乾的事情,那麼直接在task創建的時候。那麼task任務執行的順序我們基本可以確定如下:
1.構造Task任務時候的閉包,類似java類的構造函數
2.執行doFirst的閉包
3.執行action 配置的閉包
4.執行doLast的閉包
除了在build.gradle中定義task之外,我們也可以在java代碼中定義task
在android項目中,新建一個module,注意這裏選擇的是java module,必須是java的module,並且 新建的module名稱爲buildSrc(注意名字必須是buildSrc,這是gralde的保留文件夾),新建moudule之後會是下面這個樣子:
在buildSrc中定義java類,我們可以直接在module app下的build.gradle直接引用 使用,在module buildSrc中的build.gradle 配置相關依賴:
apply plugin: 'java-library'
repositories {
google()
jcenter()
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation gradleApi()
implementation 'com.android.tools.build:gradle:3.4.1'
}
sourceCompatibility = "7"
targetCompatibility = "7"
在這個文件夾中定義的類,我們可以直接在app module下的build.gradle的。我們可以自定義自己的Task,繼承DefaultTask,如下面的實現方式:
package com.android.buildsrc;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.Task;
import org.gradle.api.tasks.TaskAction;
public class MyTask extends DefaultTask {
/**
* 註解 @TaskAction 標記的方法就是 task執行時候 執行的方法
*/
@TaskAction
public void executeMyTask(){
System.err.println("MyTask execute");
}
@Override
public Task doFirst(Action<? super Task> action) {
System.err.println("MyTask 的 doFirst執行");
return super.doFirst(action);
}
@Override
public Task doLast(Action<? super Task> action) {
System.err.println("MyTask 的 doLast 執行");
return super.doFirst(action);
}
}
那麼可以在build.gradle中使用MyTask,下面的代碼定義了一個名爲xxxxx的task,Type爲剛剛在java代碼中定義的Task。
//類似java當中的使用 直接導入相關的java類
import com.android.buildsrc.MyTask
def MyTask mytask = task xxxxx(type:MyTask)
mytask.doFirst {
println('doFirst')
}
mytask.doLast {
println('doLast')
}
在我們的app module下面的build.gradle文件裏面,我們還可以定義類,方法,變量等等。例如
def getBranch() {
def stdout = new ByteArrayOutputStream();
exec {
commandLine 'java','-version'
standardOutput = stdout
}
return stdout.toString()
}
getBranch()
通過Project下的exec方法,可以執行裏面的閉包,我們可以執行命令行 的命令,這個例子是獲取當前java的運行版本。我們還可以使用git命令獲取當前的分支....使用加固命令來加固,等等都可以用這個閉包來進行操作,所有可以在定義插件或者Task,或者Transform中能夠實現的功能,都能夠直接用Groovy語言編寫的腳本在build.gradle中實現。
而且在build.gradle中配置的參數,能夠使用的參數,屬性,都對應於Project中的一個閉包,或者AppExtention中的一個閉包.....當我們記不起可以配置什麼屬性的時候,可以點進去看看有哪些參數可以配置,以AppExtention android 爲例(就是build.gradle中配置的android配置塊)都是有層級的,看看下面的這段配置:
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.android.androiddemo"
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
signingConfigs {
debug {
// No debug config
storeFile file("../store.jks")
storePassword "123456"
keyAlias "android"
keyPassword "123456"
}
release {
storeFile file("../store.jks")
storePassword "123456"
keyAlias "android"
keyPassword "123456"
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
minifyEnabled false
zipAlignEnabled false
shrinkResources false
}
release {
signingConfig signingConfigs.release
minifyEnabled true
zipAlignEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
}
我們的compileSdkVersion鼠標定位點擊,可以點擊進去,定位到BaseExtention的一個方法
可以看到都是對應一個方法,當我們不知道或者只能記住某個配置的大概名字的時候,我們就可以到這裏搜索。很方便。
例如我們打包apk的時候,系統都會生成app-debug.apk 或者app-release.apk,如果我們想對文件改個名,可以像下面這樣做:
applicationVariants.all {
variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && !outputFile.name.contains("unaligned") && variant.buildType.name == "release") {
def fileName = "${buildInfo.app_name}_v${defaultConfig.versionName}_${buildInfo.build_time}_${variant.buildType.name}.apk"
//output.outputFile就是apk的輸出文件
output.outputFile = new File(project.buildDir.absolutePath + "/outputs/apk/" + fileName)
}
}
}
其實applicationVariants也對應於AppExtention中的一個方法:
通過這個applicationVariants 我們可以獲得許多配置信息,或者修改許多配置信息。我們應該學會去利用這些源代碼,找到其中的閉包和方法,配置或者修改我們項目中的屬性或者參數。