你們好,我叫Jack馮; 本人20年碩士畢業於廣東工業大學,於2020年6月加入37手遊安卓團隊;目前主要負責海外遊戲發行安卓相關開發。html
groovy特性:java
- 基於JVM的開發語言,執行:groovy源文件==》class字節碼==》JVM處理執行;或groovy源文件直接解析執行(相似Script) - 無縫集成全部的Java類庫,但腳本寫法比Java更簡潔node
String(java.lang.String) + GString(Groovy String),經常使用定義方式有單引號、雙引號、三引號 注意: 單引號和Java的雙引號是同樣的,內容不能改變; 雙引號,支持參數擴展(實現類會變成GString),擴展的字符串能夠是任意表達式,即「 ${ 任意表達式 } 」; 三引號,格式任意,不須要轉義字符、指定輸出。android
示例代碼:正則表達式
def str = 'a single \' \' " "string'
def str2 = "a double ' ' \" \" " +
"string "
def str3 = '''a thuple ' ' " "
string'''
println str
println str2
println str3
println str.class
println str2.class
println str3.class
def str4 = "double string : ${str2}"
println str4.class
複製代碼
輸出結果:api
a single ' ' " "string
a double ' ' " " string
a thuple ' ' " "
string
class java.lang.String
class java.lang.String
class java.lang.String
class org.codehaus.groovy.runtime.GStringImpl
複製代碼
字符串擴展的方法衆多,具體來源見下圖: markdown
java.lang.String:Java原有的方法。數據結構
DefaultGroovyMethods:Groovy對全部對象的一個擴展。閉包
StringGroovyMethods:繼承自DefaultGroovyMethods,重寫了適用於String使用的方法。下面是截取的部分源碼:app
package org.codehaus.groovy.runtime;
public class StringGroovyMethods extends DefaultGroovyMethodsSupport {
...
//將字符串的第一個字母大寫的簡便方法
public static String capitalize(String self){..}
//建立一個新的CharSequence,它與這個字符串相反(向後)
public static CharSequence reverse(CharSequence self){..}
//逐行遍歷此字符串。
public static <T> T eachLine(String self, int firstLine, @ClosureParams(value=FromString.class, options={"String","String,Integer"}) Closure<T> closure){..}
//返回在字符串中第一次出現已編譯正則表達式時調用閉包的結果。
public static String find(String self, Pattern pattern, @ClosureParams(value=SimpleType.class, options="java.lang.String[]") Closure closure) {..}
...
}
複製代碼
(1) 普通類型參數使用:
def str = "groovy Hello"
//指定長度和填充字符,對已知字符的填充
println str.center(18,'a')
println str.padLeft(18,'a')
//字符串的比較操做符
def str2 = 'Hello'
println str > str2
//獲取字符串的index對應值
println str[0]
//獲取字符串的一段子串
println str[0..1]
//刪掉字串
println str - str2
//字符串反向輸出
println str.reverse()
//字符串首字母大寫
println str.capitalize()
//字符串是否數字的判斷
println str.isNumber()
//字符串轉Integer類型/Double類型等
def str3 = "123"
println str3.toInteger()
println str3.toInteger().class
複製代碼
(2)和閉包組合:下面示例就是在閉包(task類型)傳入一個字符串參數路徑,執行find閉包方法輸出。
task findFile{
String path = getRootDir().absolutePath+File.separator+"build.gradle"
path.find { String filePath ->
File file = new File(filePath)
if (file.exists()){
println "build.gradle in rootDir exists!"
}
}
}
//對比常規閉包的定義和使用
def findFile = {...}
findFile.call()
複製代碼
輸出結果:
10:15:29: Executing task 'findFile'...
初始化開始...
> Configure project :
配置階段完成
> Configure project :Project01
build.gradle in rootDir exists!
> Task :Project01:findFile UP-TO-DATE
gradle執行結束
BUILD SUCCESSFUL in 93ms
10:15:30: Task execution finished 'findFile'.
複製代碼
關於Groovy和Java的異同,除了所述的字符串外,還有自動導入包的方式、方法調用時期差別等,具體可見Groovy文檔《 Differences with Java》 www.groovy-lang.org/differences…
閉包,實質上是一段代碼塊。 這裏介紹閉包基礎部分,主要包括內容:
定義和調用:參數 ->執行體
//(1)閉包定義
def clouser = { String name ->
println "一、println:clouser ${name}"
}
//(2)調用方式
clouser("test")
clouser.call("test call")
//(3)多個參數
def MyClouser = {String names ,int ages ->
println "二、println MyClouser:heloo ${names}, my ages is ${ages} "
}
def names = 'my_clouser'
MyClouser(names,100)
//(4)全部閉包的隱式默認參數it,能夠不聲明的
def itClouser = {
println "三、println itClouser: hello ${it}"
}
itClouser('it_clouser')
//(5)閉包的返回值,沒有return的話,返回null
def returnClouser = {
println "四、println returnClouser:hello ${it}"
// return "hello ${it}"
}
def result = returnClouser('return_clouser')
println "五、println returnClouser result:"+result
複製代碼
輸出結果:
一、println:clouser test
一、println:clouser test call
二、println MyClouser:heloo my_clouser, my ages is 100
三、println itClouser: hello it_clouser
四、println returnClouser:hello return_clouser
五、println returnClouser result:null
複製代碼
舉例:字符串與閉包的結合使用
String str = 'the 2 and 3 is 5'
//一、each的遍歷
str.each {
String temp -> print temp.multiply(2)//每一個字符拷貝一份,返回值仍是str自己
}
println ""
//二、find來查找符合條件的第一個
println str.find {
String s -> s.isNumber()
}
//三、查找全部符合條件的
def list = str.findAll {
String s -> s.isNumber()
}
println list.toListString()
//四、查找是否有符合條件的
def anyresult = str.any {
String s -> s.isNumber()
}
println anyresult
//五、查找是否所有都符合條件
def everyresult = str.every {
String s -> s.isNumber()
}
println everyresult
//六、將小寫字母轉換爲大寫
def list2 = str.collect {
it.toUpperCase()
}
println list2.toListString()
複製代碼
輸出結果:
tthhee 22 aanndd 33 iiss 55
2
[2, 3, 5]
true
false
[T, H, E, , 2, , A, N, D, , 3, , I, S, , 5]
複製代碼
1) 在介紹閉包委託策略以前,這裏先介紹下閉包的三個重要變量。
若是是在類或方法中定義閉包時,三個變量(this、owner、delegate)的值是同樣的;
可是在閉包中嵌套定義了閉包,this和owner、delegate指向的值就會不一樣,若是單獨修改delegate變量指向,則三者值都會不同。
這裏在類或方法中定義閉包,
def scriptClouser = {
println "scriptClouser this : " + this
println "scriptClouser owner : " + owner
println "scriptClouser delegate : " + delegate
}
scriptClouser.call()
複製代碼
輸出結果:
scriptClouser this : pkg.character01@5be067de
scriptClouser owner : pkg.character01@5be067de
scriptClouser delegate : pkg.character01@5be067de
複製代碼
2)內部類相關
//定義內部類
class Person{
def static classClouser = {
println "classClouser this : " + this
println "classClouser owner : " + owner
println "classClouser delegate : " + delegate
}
def static say(){
def methodClouser = {
println "methodClouser this : " + this
println "methodClouser owner : " + owner
println "methodClouser delegate : " + delegate
}
methodClouser.call()
}
}
//一、輸出person的static方法調用結果,三者都是指向當前的類
Person.classClouser.call()
Person.say()
複製代碼
輸出結果:
classClouser this : class pkg.Person
classClouser owner : class pkg.Person
classClouser delegate : class pkg.Person
methodClouser this : class pkg.Person
methodClouser owner : class pkg.Person
methodClouser delegate : class pkg.Person
複製代碼
若是去掉方法的static聲明,則輸出的person指向會是當前類的某個具體對象。
class Person{
def classClouser = {...}
def say(){...}
}
//二、非static方法調用示例
Person innerPerson = new Person()
innerPerson.classClouser.call()
innerPerson.say()
複製代碼
輸出結果:
classClouser this : pkg.Person@5df417a7
classClouser owner : pkg.Person@5df417a7
classClouser delegate : pkg.Person@5df417a7
methodClouser this : pkg.Person@5df417a7
methodClouser owner : pkg.Person@5df417a7
methodClouser delegate : pkg.Person@5df417a7
複製代碼
3)特殊情形:閉包中的閉包
這裏,this指向定義閉包的類;owner指向nestClouser的實例對象,delegate指向最近的閉包對象。其中,能夠單獨指定innerClouser的delegate,示例以下。
//閉包中定義一個閉包,三者不一致
def nestClouser = {
def innerClouser = {
println "innerClouser this : " + this
println "innerClouser owner : " + owner
println "innerClouser delegate : " + delegate
}
//innerClouser.delegate = innerPerson
innerClouser.call()
}
nestClouser.call()
複製代碼
輸出結果:
innerClouser this : pkg.character01@224b4d61
innerClouser owner : pkg.character01$_run_closure1@5ab14cb9
innerClouser delegate : pkg.character01$_run_closure1@5ab14cb9
複製代碼
單獨指定delegate的話,三者的輸出結果都會不同:
innerClouser this : pkg.character01@7c041b41
innerClouser owner : pkg.character01$_run_closure1@361c294e
innerClouser delegate : pkg.Person@7859e786
複製代碼
閉包中委託策略分四種:OWNER_FIRST(默認)、DELEGATE_FIRST、OWNER_ONLY、DELEGATE_ONLY,默認策略代表閉包中的變量、方法等,都是首先從owner指向的對象處尋找。經過改變delegate指向對象和不一樣的委託策略指定,能夠指定閉包優先從哪一個對象尋找調用的變量和方法。
下面示例修改委託策略爲Closure.DELEGATE_FIRST,可以使得優先從delegate指向的對象中尋找同名的變量方法屬性,找不到再返回Owner指向對象中查詢。
class ClosureOutput{
String name
def method = { "The output of this time is ${name}" }
String toString(){
method.call()
}
}
class ClosureDelegationOutput{
String name
}
def output01 = new ClosureOutput(name:'ClosureOutput')
def output02 = new ClosureDelegationOutput(name:'ClosureDelegationOutput')
println "output01:"+output01.toString()
//修改delegate對象,添加委託策略,從delegate開始尋找
output01.method.delegate = output02
output01.method.resolveStrategy = Closure.DELEGATE_FIRST
println "output01:"+output01.toString()
複製代碼
輸出結果:
output01:The output of this time is ClosureOutput
output01:The output of this time is ClosureDelegationOutput
複製代碼
注意:若是ClosureDelegationOutput方法中沒有ClosureOutput方法的同名參數方法,並且修改的委託策略是Closure.DELEGATE_ONLY,會拋出異常groovy.lang.MissingPropertyException。
Gradle的執行流程,主要分爲三個階段
爲了方便追蹤各個階段的執行狀況,在各節點加了日誌打印。
首先是初始化階段,執行settings.gradle進行全局配置,在文件添加:
println '初始化開始...'
複製代碼
而後是配置、執行階段的監聽。在根目錄build.gradle添加:
//配置階段監聽(本project)
this.beforeEvaluate { Project project ->
println "$project 配置階段開始 ..."
}
this.afterEvaluate { Project project ->
println "$project 配置完成 ..."
}
//配置階段監聽(包括其餘project)
this.gradle.beforeProject { Project project ->
println " $project 準備配置 ..."
}
this.gradle.afterProject { Project project ->
println " $project 配置結束 ..."
}
//配置完成
gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
println "配置階段結束,TaskExecutionGraph is ready ..."
if(taskGraph.hasTask(taskZ)) {
lib1.dependsOn taskZ
}
}
//執行階段的監聽
gradle.taskGraph.beforeTask { Task task ->
println " $task 開始執行..."
}
gradle.taskGraph.afterTask { Task task, TaskState state ->
if (state.failure) {
println " $task 執行失敗..."
}
else {
println " $task 執行結束..."
}
}
//執行階段結束後的回調監聽,操做個別文件
this.gradle.buildFinished {
println '執行階段結束'
fileTree('/project01/build/libs/'){ FileTree fileTree ->
fileTree.visit { FileTreeElement fileTreeElement ->
copy {
from fileTreeElement.file
into getRootProject().getBuildDir().path + '/testFiletree/'
}
}
}
}
複製代碼
還能夠添加其餘監聽:
//this.gradle.addListener()
//this.gradle.addProjectEvaluationListener()
this.gradle.addBuildListener(new BuildListener() {
@Override
void buildStarted(Gradle gradle) {
println '開始構建'
}
@Override
void settingsEvaluated(Settings settings) {
println 'settings.gradle 中代碼執行完畢'
}
@Override
void projectsLoaded(Gradle gradle) {
println '初始化階段結束'
}
@Override
void projectsEvaluated(Gradle gradle) {
println '配置階段結束,TaskExecutionGraph is ready ...'
}
@Override
void buildFinished(BuildResult buildResult) {
println '執行階段結束 '
}
})
複製代碼
執行結果:
11:57:45: Executing task 'clean'...
初始化開始...
> Configure project :
root project 'helloGradle' 配置結束 ...
root project 'helloGradle' 配置完成 ...
> Configure project :buildApp
project ':buildApp' 準備配置 ...
project ':buildApp' 配置結束 ...
> Configure project :Project01
project ':Project01' 準備配置 ...
project ':Project01' 配置結束 ...
> Configure project :project02
project ':project02' 準備配置 ...
project ':project02' 配置結束 ...
配置階段結束,TaskExecutionGraph is ready ...
> Task :clean UP-TO-DATE
task ':clean' 開始執行...
task ':clean' 執行結束...
> Task :buildApp:clean UP-TO-DATE
task ':buildApp:clean' 開始執行...
task ':buildApp:clean' 執行結束...
> Task :Project01:clean UP-TO-DATE
task ':Project01:clean' 開始執行...
task ':Project01:clean' 執行結束...
> Task :project02:clean UP-TO-DATE
task ':project02:clean' 開始執行...
task ':project02:clean' 執行結束...
執行階段結束
BUILD SUCCESSFUL in 42ms
4 actionable tasks: 4 up-to-date
11:57:45: Task execution finished 'clean'.
複製代碼
執行完畢,project01/build/libs/下的文件已拷貝到根目錄中。
對於生命週期的監聽,更可能是爲了在編譯過程或者結束階段,添加一些自定義操做,例如重命名APK等。對比其餘構建工具,無法輕易作到像Gradle這樣,見縫插針式地監聽生命週期並執行自定義操做。
...
android {
defaultConfig {
applicationId "com.game.demo"
minSdkVersion rootProject.ext.androidMinSdkVersion
targetSdkVersion rootProject.ext.androidTargetSdkVersion
versionCode 35
versionName "1.0.1"
}
//處理apk名稱
...
applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "${defaultConfig.applicationId}-${defaultConfig.versionName}-${variant.buildType.name}.apk"
}
}
...
}
複製代碼
歸納其執行流程:
task和project都是Gradle比較重要的概念,task即任務,是構建過程執行的基本工做。Android Studio(Windows環境)可使用指令「gradlew tasks」查看當前工程的task詳細信息。當Gradle API自帶task沒法知足項目須要時,能夠自定義task執行特定操做。例如,在工程的不一樣模塊gradle文件,自定義task,是能夠相互調用的。例如在test1.gradle定義了test(),可在test2.gradle中調用,注意執行順序會有差異(後面在task執行順序中講解)。
task createTask(group: '37sdk',description:'task study'){
//在執行階段輸出
doFirst {
println 'this group : ' + group
println 'Task created successfully。'
}
}
複製代碼
輸出:(執行完畢能夠在獨立文件夾37sdk管理自定義task)
...
> Task :createTask
task ':createTask' 開始執行...
this group : 37sdk
Task created successfully
task ':createTask' 執行結束...
...
複製代碼
更多:build下面有不少assembleXxx任務,是根據buildType和productFlavor的不一樣自動建立多個。
建立方式2:經過taskContainer去建立,而後在閉包中配置屬性
this.tasks.create(name:'helloTask2'){
setGroup('37sdk')
setDescription('task study')
setBuildDir('build/outputs/helloTask3/')
println 'hello task2'
}
複製代碼
可指定的參數類型,見Task.class:
public interface Task extends Comparable<Task>, ExtensionAware {
//可指定參數及對應方法
String TASK_NAME = "name";
String TASK_DESCRIPTION = "description";
String TASK_GROUP = "group";
String TASK_TYPE = "type";
String TASK_DEPENDS_ON = "dependsOn";
String TASK_OVERWRITE = "overwrite";
String TASK_ACTION = "action";
String TASK_CONSTRUCTOR_ARGS = "constructorArgs";
void setGroup(@Nullable String var1);
void setDescription(@Nullable String var1);
void setDependsOn(Iterable<?> var1);
void setProperty(String var1, Object var2) throws MissingPropertyException;
...
}
複製代碼
注意:若指定task輸出目錄,調用的是Project的方法,見Project.class:
public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
//gradle默認配置
String DEFAULT_BUILD_FILE = "build.gradle";
String PATH_SEPARATOR = ":";
String DEFAULT_BUILD_DIR_NAME = "build";
String GRADLE_PROPERTIES = "gradle.properties";
String SYSTEM_PROP_PREFIX = "systemProp";
String DEFAULT_VERSION = "unspecified";
String DEFAULT_STATUS = "release";
//更多的可指定配置
void setBuildDir(Object var1);
void setDescription(@Nullable String var1);
void setGroup(Object var1);
void setVersion(Object var1);
void setStatus(Object var1);
...
//經常使用方法
Project getRootProject();
File getRootDir();
File getBuildDir();
...
}
複製代碼
task的邏輯可運行在配置階段和執行階段(應用閉包 doFirst{ } 和 doLast{ } );另外,同是執行階段,不一樣調用方式的執行順序會有差異。
示例代碼:
// 使用 Task 在執行階段進行操做
task myTask3(group: "MyTask", description: "task3") {
doFirst {
// 次執行
println "the current order is 2"
}
println "這是一條運行在配置階段的,myTask3"
doLast {
// 最後執行
println "the current order is 3"
}
}
// 也可使用 taskName.doxxx 的方式添加執行任務
myTask3.doFirst {
// 這種方式的最早執行
println "the current order is 1"
}
複製代碼
執行結果以下:
16:37:13: Executing task 'myTask3'...
初始化開始...
> Configure project :
這是一條運行在配置階段的,myTask3
root project 'helloGradle' 配置完成 ...
project ':buildApp' 配置結束 ...
project ':Project01' 配置結束 ...
project ':project02' 配置結束 ...
配置階段結束,TaskExecutionGraph is ready ...
> Task :myTask3
task ':myTask3' 開始執行...
the current order is 1
the current order is 2
the current order is 3
task ':myTask3' 執行結束...
執行階段結束
BUILD SUCCESSFUL in 96ms
1 actionable task: 1 executed
16:37:13: Task execution finished 'myTask3'.
複製代碼
示例2:自定義task去計算執行階段的耗時,即計算build執行時長,區間:preBuildTask.doFirst--buildTask.doLast
//注意1:爲何運行在this.afterEvaluate 監聽去計算build時長?由於是配置結束階段,依賴藍圖已經輸出,能夠查找到每個task
//注意2:保證要找的task已經配置完畢,prebuild是在Android工程裏面有
def startBuildTime,endBuildTime
this.afterEvaluate { Project project ->
def preBuildTask = project.tasks.getByName('preBuild')
preBuildTask.doFirst {
startBuildTime = System.currentTimeMillis()
println 'the start time is ' + startBuildTime
}
def buildTask = project.tasks.getByName('build')
buildTask.doLast {
endBuildTime = System.currentTimeMillis()
println "the end time is ${endBuildTime - startBuildTime}"
}
}
複製代碼
task的執行階段,指定執行順序有兩種方式
/添加依賴的方式
task taskX{
doLast {
println 'taskX'
}
}
task taskY{
doLast {
println 'taskY'
}
}
taskY.dependsOn(taskX)
task taskZ(dependsOn:[taskX,taskY]){
// dependsOn this.tasks.findAll {
// task ->return task.name.startsWith('lib')
// }
doLast {
println 'taskZ'
}
}
複製代碼
輸出結果:
17:13:30: Executing task 'taskZ'...
初始化開始...
> Configure project :
root project 'helloGradle' 配置完成 ...
project ':buildApp' 配置結束 ...
project ':Project01' 配置結束 ...
project ':project02' 配置結束 ...
配置階段結束,TaskExecutionGraph is ready ...
> Task :taskX
task ':taskX' 開始執行...
taskX
task ':taskX' 執行結束...
> Task :taskY
task ':taskY' 開始執行...
taskY
task ':taskY' 執行結束...
> Task :taskZ
task ':taskZ' 開始執行...
taskZ
task ':taskZ' 執行結束...
執行階段結束
BUILD SUCCESSFUL in 81ms
3 actionable tasks: 3 executed
17:13:30: Task execution finished 'taskZ'.
複製代碼
執行taskZ,就會提早執行taskX、taskY;相似的,執行taskY也會先執行taskX。依賴的效果,首先執行所依賴的task,再到本task。同比Java,若是類A依賴類B,類B會先被編譯,而後纔是類A。依賴的目的,在執行階段添加本身的操做,例如建立lib系列的task任務,當執行到taskZ時,先把lib系列任務先執行,而後纔是taskZ自身任務。
關於task依賴拓撲圖,能夠引入插件gradle-visteg,以圖的形式輸出Task相關依賴,默認生成visteg.dot文件;使用指令dot -Tpng ./visteg.dot -o ./visteg.dot.png,可將其轉換圖片格式查看。 (1)首先在根目錄build.gradle配置倉庫路徑
buildscript {
repositories {
google()
jcenter()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.2'
classpath 'gradle.plugin.cz.malohlava:visteg:1.0.5'
}
}
複製代碼
(2)應用插件
apply plugin: 'cz.malohlava.visteg'
複製代碼
(3)visteg屬性配置,重要是enabled(啓用插件)和destination(輸出文件路徑)
visteg {
enabled = true
colouredNodes = true
colouredEdges = true
destination = 'build/reports/visteg.dot'
exporter = 'dot'
colorscheme = 'spectral11'
nodeShape = 'box'
startNodeShape = 'hexagon'
endNodeShape = 'doubleoctagon'
}
複製代碼
(4)執行上方示例taskZ,在路徑下能夠查看dot文件:/build/reports/visteg.dot
digraph compile {
colorscheme=spectral11;
rankdir=TB;
splines=spline;
":app:taskZ" -> ":app:taskX" [colorscheme="spectral11",color=5];
":app:taskZ" -> ":app:taskY" [colorscheme="spectral11",color=5];
":app:taskZ" [shape="hexagon",colorscheme="spectral11",style=filled,color=5];
":app:taskX" [shape="doubleoctagon",colorscheme="spectral11",style=filled,color=5];
":app:taskY" -> ":app:taskX" [colorscheme="spectral11",color=5];
":app:taskY" [shape="box",colorscheme="spectral11",style=filled,color=5];
{ rank=same; ":app:taskZ" }
}
複製代碼
(4)轉換命令:dot -Tpng ./visteg.dot -o ./visteg.dot.png
這裏經過腳本操做AndroidManifest.xml文件,去修改APK的版本號、圖標、活動主題等內容,以及新增參數如等。掌握了一種可行修改方式後,其餘處理也能夠依樣進行。
task replaceManifest(group: "gradleTask", description: "replace") {
GPathResult androidManifest = new XmlSlurper().parse("${projectDir}/src/main/AndroidManifest.xml")
//一、修改版本號beta
String versionName = androidManifest['@android:versionName']
//注意:等同於androidManifest['@android:versionName'];另外,若是build.gradle的defaultConfig標籤有設定version信息,最後構建優先選擇配置文件的指定版本。
if(!versionName.contains('-beta')){
versionName += '-beta'
androidManifest.setProperty('@android:versionName', versionName + "")
}
//二、替換圖標
//String iconName = androidManifest.application['@android:icon']
def iconName = "@drawable/logo37"
androidManifest.application.setProperty('@android:icon', iconName + "")
//三、替換活動主題:按聲明activity的順序ID修改
def activityTheme = androidManifest.application.'activity'[0]['@android:theme']
println "'activity'[0]['@android:theme']="+activityTheme
def newTheme = "@style/Theme.AppCompat.NoActionBar"
androidManifest.application.activity[0].setProperty('@android:theme',newTheme + "")
//三、替換主活動主題
def newTheme2 = "@style/Theme.AppCompat.DayNight.DarkActionBar"
androidManifest.application.activity.each{
def isReplaceMainActivityTheme = false
it.children().each {
if(it.name() == "intent-filter"){
it.children().each{
if(it.name()=="action" && it.@"android:name"=="android.intent.action.MAIN"){
isReplaceMainActivityTheme = true
return true
}
}
}
if(isReplaceMainActivityTheme){
return true
}
}
if (isReplaceMainActivityTheme){
it.@"android:theme" = newTheme2
return true
}
}
new File(("${projectDir}/src/main/AndroidManifest.xml")).write(XmlUtil.serialize(androidManifest))
}
複製代碼
除了自定義task這種方式之外,也能夠在Gradle生命週期的方法中執行腳本,示例:在AndroidManifest.xml中添加參數。同理,補充權限聲明也是一樣的方式等
project.afterEvaluate {
android.applicationVariants.all { ApplicationVariant variant ->
String variantName = variant.name.capitalize()
def processManifestTask = project.tasks.getByName("process${variantName}Manifest")
processManifestTask.doLast { pmt ->
String manifestPath = "${projectDir}/src/main/AndroidManifest.xml"
def manifest = file(manifestPath).getText()
def xml = new XmlParser().parseText(manifest)
xml.application[0].appendNode("meta-data", ['android:name': 'com.facebook.sdk.ApplicationId', 'android:value': '@string/facebook_app_id'])
new File(("${projectDir}/src/main/AndroidManifest.xml")).write(XmlUtil.serialize(xml))
}
}
}
複製代碼
plugin自己的新東西並很少,主要是封裝的一個體現。Gradle plugin插件,就是將完成特定任務的全部Task都封裝到一個插件中,當別人引用這個插件,就能夠完成特定的功能。
腳本插件:實爲腳本,做用是可拆分複雜腳本、封裝任務,例如拆分配置.gradle、修改編譯打包路徑等。引入方式示例:
apply from: "../libconfig.gradle"
複製代碼
二進制插件:腳本打成jar包等形式,已發佈到倉庫(maven等),常見的Java插件(生成jar包)、Android插件(生成apk、aar)等。引入方式示例:
apply plugin: 'com.android.application'
apply plugin: 'groovy'
...
複製代碼
根目錄build.gradle文件中,標籤buildscript可爲該項目配置構建相關路徑,參數是Closure。dependencies是添加編譯依賴項的,repositories是爲腳本依賴項配置存儲庫。他們的配置都是用閉包的形式。
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
//等價於:implementation group: 'com.android.tools.build', name: 'gradle', version: '3.4.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
複製代碼
更多插件類型:
首先建立module,若是命名爲buildSrc,在本地工程中能夠直接引入使用自建立的plugin;固然,發佈到倉庫供給他人使用的話就不用考慮這個命名限制。
這裏建立MyPlugin.groovy,實現一個沒有任何功能的插件。
import org.gradle.api.Plugin
import org.gradle.api.Project
//自定義插件
class MyPlugin implements Plugin<Project>{
@Override
void apply(Project project){
println 'pluginTest...' + project.name
}
}
複製代碼
而後在resources/META-INF.gradle-plugins/com.game.plugin.testPlugin.properties
implementation-class=com.game.testPlugin.MyPlugin
複製代碼
//提交倉庫到本地目錄
uploadArchives {
repositories {
mavenDeployer {
pom.groupId = 'com.game.plugin'
pom.artifactId = 'testPlugin'
pom.version = '1.0.1'
repository(url: uri('../LocalRepo'))
}
}
}
複製代碼
再者,在根目錄build.gradle,提供插件路徑
buildscript {
repositories {
google()
jcenter()
maven {
url uri('/LocalRepo')//添加依賴倉庫
}
}
dependencies {
classpath "com.android.tools.build:gradle:3.4.1"
//依賴插件路徑格式classpath '[groupId]:[artifactId]:[version]'
classpath "com.game.plugin:testPlugin:1.0.1"
}
}
複製代碼
在項目模塊的build.gradle添加引用,
apply plugin: 'com.game.plugin.testPlugin'
複製代碼
一、編寫gradle插件,比較重要的是對gradle生命週期的掌握,才能正確地去作自定義操做。生命週期的初始化階段,完成全部工程的初始化,決定整個項目有多少子項目,重點是解析build.gradle文件;而後是配置階段,build.gradle的代碼基本都是運行在配置階段,配置結束就開始真正執行task任務邏輯。
二、gradle核心模塊的project,是腳本代碼的入口,全部腳本代碼實際都編寫在project的實例中,每個build.gradle對應一個project的實例,在build.gradle能夠定位文件、獲取root工程和管理子工程以及管理依賴;task纔是真正執行邏輯的角色,可指定執行順序和依賴,以插入自定義的task來完成特定功能,例如tinker將本身的task掛接到gradle生命週期的中間,去完成本身的功能。