作者

大家好,我叫Jack冯; 自己20年硕士结业于广东工业大学,于2020年6月参加37手游安卓团队;目前首要担任海外游戏发行安卓相关开发。

一、Gradle简介

  • Gradle是什么,能做什么?
  • Android Studio的默许构建东西,用来构建运用程序。组成部分见下图:

安卓-Gradle浅谈

  • groovy中心语法:闭包、数据结构等
  • build script block:工程build.gradle的脚本
  • gradle api:project、task、plugin等

二、Groovy根底

groovy特性:

– 基于JVM的开发言语,履行:groovy源文件==》class字节码==》JVM处理履行;或groovy源文件直接解析履行(相似Script) – 无缝集成一切的Java类库,但脚本写法比Java更简洁

1、字符串

(1)界说运用

String(java.lang.String) + GString(Groovy String),常用界说办法有单引号、双引号、三引号 留意: 单引号和Java的双引号是相同的,内容不能改动; 双引号,支持参数扩展(完结类会变成GString),扩展的字符串可所以恣意表达式,即“ ${ 恣意表达式 } ”; 三引号,格局恣意,不需要转义字符、指定输出。

示例代码:

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

输出成果:

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

2、扩展

字符串扩展的办法很多,详细来历见下图:

安卓-Gradle浅谈

  • java.lang.String:Java原有的办法。

  • DefaultGroovyMethods:Groovy对一切目标的一个扩展。

  • StringGroovyMethods:继承自DefaultGroovyMethods,重写了适用于String运用的办法。下面是截取的部分源码:

    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…

2、闭包根底

闭包,实质上是一段代码块。 这儿介绍闭包根底部分,首要包括内容:

  • 闭包概念:闭包的界说、调用
  • 闭包参数:一般参数、隐式参数
  • 闭包回来值:总是有回来值的

(1)界说

界说和调用:参数 ->履行体

//(1)闭包界说
def clouser = { String name ->
    println "1、println:clouser ${name}"
}
//(2)调用办法
clouser("test")
clouser.call("test call")
//(3)多个参数
def MyClouser = {String names ,int ages ->
    println "2、println MyClouser:heloo ${names}, my ages is ${ages} "
}
def names = 'my_clouser'
MyClouser(names,100)
//(4)一切闭包的隐式默许参数it,能够不声明的
def itClouser = {
    println "3、println itClouser: hello ${it}"
}
itClouser('it_clouser')
//(5)闭包的回来值,没有return的话,回来null
def returnClouser = {
    println "4、println returnClouser:hello ${it}"
//    return "hello ${it}"
}
def result = returnClouser('return_clouser')
println "5、println returnClouser result:"+result

输出成果:

1、println:clouser test
1、println:clouser test call
2、println MyClouser:heloo my_clouser, my ages is 100 
3、println itClouser: hello it_clouser
4、println returnClouser:hello return_clouser
5、println returnClouser result:null

(2)运用

安卓-Gradle浅谈

举例:字符串与闭包的结合运用

String str = 'the 2 and 3 is 5'
//1、each的遍历
str.each {
    String temp -> print temp.multiply(2)//每个字符复制一份,回来值仍是str本身
}
println ""
//2、find来查找契合条件的第一个
println str.find {
    String s -> s.isNumber()
}
//3、查找一切契合条件的
def list =  str.findAll {
    String s -> s.isNumber()
}
println list.toListString()
//4、查找是否有契合条件的
def anyresult = str.any {
    String s -> s.isNumber()
}
println anyresult
//5、查找是否全部都契合条件
def everyresult = str.every {
    String s -> s.isNumber()
}
println everyresult
//6、将小写字母转换为大写
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]

(3)闭包变量

1) 在介绍闭包托付战略之前,这儿先介绍下闭包的三个重要变量。

假如是在类或办法中界说闭包时,三个变量(this、owner、delegate)的值是相同的;

但是在闭包中嵌套界说了闭包,this和owner、delegate指向的值就会不同,假如独自修正delegate变量指向,则三者值都会不相同。

  • this,代表闭包界说处的类,不行修正
  • owner,代表闭包界说处的类或者目标,不行修正
  • delegate,代表恣意目标,默许和owner一致,可修正

这儿在类或办法中界说闭包,

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()
    }
}
//1、输出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(){...}
}
//2、非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

(4)托付战略

闭包中托付战略分四种: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。

三、生命周期

1、履行阶段

Gradle的履行流程,首要分为三个阶段

安卓-Gradle浅谈

  • Initialization初始化阶段:解析整个工程中一切project,构建一切的project目标
  • Configuration装备阶段:解析一切project目标的task,构建一切task的依靠图
  • Execution履行阶段:履行详细的task及其依靠的task

2、监听示例

为了方便追寻各个阶段的履行情况,在各节点加了日志打印。

首要是初始化阶段,履行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/下的文件已复制到根目录中。

安卓-Gradle浅谈

3、拓展

关于生命周期的监听,更多是为了在编译进程或者完毕阶段,增加一些自界说操作,例如重命名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"
        }
    }
    ...
}

概括其履行流程:

  • 首要,settings.gradle是gradle履行的入口,首要获取项目模块的相关信息,初始化。
  • 然后,先后装备root.project、子project,装备的进程便是获取build.gradle的参数、task信息。
  • 接着,装备完毕调用 project.afterEvaluate,它表示一切的模块都现已装备完毕,预备进入履行阶段。
  • 此时,所谓的有向无环图现已输出,包含 task 及其依靠的 task。在gradle.taskGraph.whenReady{}能够修正task依靠关系等。
  • 最终,履行指定的 task 及其依靠的 task。

四、Task

task和project都是Gradle比较重要的概念,task即使命,是构建进程履行的根本作业。Android Studio(Windows环境)能够运用指令“gradlew tasks”检查当时工程的task详细信息。当Gradle API自带task无法满足项目需要时,能够自界说task履行特定操作。例如,在工程的不同模块gradle文件,自界说task,是能够彼此调用的。例如在test1.gradle界说了test(),可在test2.gradle中调用,留意履行次序会有不同(后边在task履行次序中解说)。

1、界说运用

  • 创立办法1:直接经过task函数构建,创立时填充根本装备。eg:增加task的存放位置37sdk
 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();    
       ... 
    }

2、履行

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}"
    }
}

3、task依靠

task的履行阶段,指定履行次序有两种办法

  • 经过Task的API指定履行次序(即doFirst、doLast)
  • dependsOn强依靠办法
/增加依靠的办法
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自身使命。

4、拓扑图

关于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

安卓-Gradle浅谈

5、运用实例

这儿经过脚本操作AndroidManifest.xml文件,去修正APK的版本号、图标、活动主题等内容,以及新增参数如等。把握了一种可行修正办法后,其他处理也能够依样进行。

task replaceManifest(group: "gradleTask", description: "replace") {
    GPathResult androidManifest = new XmlSlurper().parse("${projectDir}/src/main/AndroidManifest.xml")
    //1、修正版本号beta
    String versionName = androidManifest['@android:versionName']
    //留意:等同于androidManifest['@android:versionName'];别的,假如build.gradle的defaultConfig标签有设定version信息,最终构建优先选择装备文件的指定版本。
    if(!versionName.contains('-beta')){
        versionName += '-beta'
        androidManifest.setProperty('@android:versionName', versionName + "")
    }
    //2、替换图标
    //String iconName = androidManifest.application['@android:icon']
    def iconName = "@drawable/logo37"
    androidManifest.application.setProperty('@android:icon', iconName + "")
    //3、替换活动主题:按声明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 + "")
    //3、替换主活动主题
    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

plugin本身的新东西并不多,首要是封装的一个体现。Gradle plugin插件,便是将完结特定使命的一切Task都封装到一个插件中,当他人引证这个插件,就能够完结特定的功用。

1、插件类型

脚本插件:实为脚本,作用是可拆分复杂脚本、封装使命,例如拆分装备.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()  
    }
}

更多插件类型:

  • 运用程序插件,插件id为com.android.application,会生成一个APK。
  • 库插件,插件id为com.android.library,会生成一个AAR,供给应其他运用程序模块用。
  • 测试插件,插件id为com.android.test,用于测试其他的模块。
  • feature插件,插件id为com.android.feature,创立Android Instant App时需要用到的插件。
  • Instant App插件,插件id为com.android.instantapp,是Android Instant App的入口。

2、插件创立

首要创立module,假如命名为buildSrc,在本地工程中能够直接引进运用自创立的plugin;当然,发布到库房供应他人运用的话就不必考虑这个命名限制。

这儿创立MyPlugin.groovy,完结一个没有任何功用的插件。

  • apply办法:插件被引进时需要履行的办法,能够自界说task操作
  • Project参数:引进当时插件的project
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'

六、总结

1、编写gradle插件,比较重要的是对gradle生命周期的把握,才干正确地去做自界说操作。生命周期的初始化阶段,完结一切工程的初始化,决定整个项目有多少子项目,重点是解析build.gradle文件;然后是装备阶段,build.gradle的代码根本都是运行在装备阶段,装备完毕就开端真实履行task使命逻辑。

2、gradle中心模块的project,是脚本代码的入口,一切脚本代码实践都编写在project的实例中,每一个build.gradle对应一个project的实例,在build.gradle能够定位文件、获取root工程和管理子工程以及管理依靠;task才是真实履行逻辑的人物,可指定履行次序和依靠,以插入自界说的task来完结特定功用,例如tinker将自己的task挂接到gradle生命周期的中间,去完结自己的功用。