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: ./gradlew 和gradle的區(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è)元素賦值
- 定義一個(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
- 根build.gradle 定義的變量,其他的子build.gradle中都能訪問(wèn)。如:
project.ext {
versionCode2=2021
}
project.ext 傳入一個(gè)閉包,返回一個(gè)Defaultextrapropertiesextension 拓展,此類實(shí)際上是用一個(gè)Map保存變量的。
- 導(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的自定義插件