第一部分:Groovy语法
变量的类型和定义
Groovy所有类型都是对象类型:
int x = 10
println x.class
double y = 3.14
println y.class
def 定义变量:
def str = "dddd"
println str.class
字符串
字符串:
// 单引号 双引号 三引号 字符串模板插入变量 全部支持
def str1 = 'aaaaa\'aa\'aaaa'
println str1
println str1.classdef str2 = '''\
第一行
第二行
第三行
'''
println str2def name = "Android"
def sayHello = "hello: ${name}"
println sayHelloprintln "2+3 == ${2+3}"/* ==================字符串的方法=================== */
// 除了支持java的所有字符串方法外,还支持下面的
def str3 = "groovy"
println str3.center(10, 'a') // 两边以字母a填充,总共长度是10 第二个参数不传是以空格填充
println str3.padLeft(10, 'a')// 左边以字母a填充,总共长度是10
def str4 = 'Hello'
println str3 > str4 // 支持字符串直接比较操作符
println str4[0] // 按下标索引字符
println str4[0..1]
def s1 = "Hello groovy"
println s1 - str3 // 支持直接字符串减法操作符println str3.reverse() // 反转字符串
println str3.capitalize() // 首字母大写class Demo{static void main(args) {println "hello Groovy"}
}
switch 和 for 语法
switch 语法:
def x = 1.23
def result
// Groovy 中的 switch-case 可以是任何对象
switch (x) {case 'foo':result = 'found foo'breakcase 'bar':result = 'bar'breakcase [1.23, 4, 5, 6, 'inlist']: //列表result = 'list'breakcase 12..30:result = 'range' //范围breakcase Integer:result = 'integer'breakcase BigDecimal:result = 'big decimal'breakdefault: result = 'default'
}println "result == ${result}"
for 循环:
/**
* 对范围的for循环
*/
def sum = 0
for (i in 0..9) {sum += i
}
println "sum == ${sum}"
sum = 0
/**
* 对List的循环
*/
for (i in [1, 2, 3, 4, 5, 6, 7, 8, 9]) {sum += i
}
println "sum == ${sum}"
/**
* 对Map进行循环
*/
for (i in ['lili': 1, 'luck': 2, 'xiaoming': 3]) {sum += i.value
}
println "sum == ${sum}"
闭包
闭包:
// 闭包定义 闭包一定有返回值,如果不写return, 则返回值为null
def clouser = { String name, int age ->println "my name is ${name} age is ${age}"return "result"
}
println clouser("小明", 22)// 闭包默认参数it
def clouser2 = { it ->println "my name is ${it}"
}
clouser2("小红")// 求阶乘
def static fab(int number) {def result = 11.upto(number) {num -> result *= num } // 通过闭包return result
}
// 通过downto实现求阶乘
def static fab2(int number) {def result = 1number.downto(1) {num -> result *= num } // 通过闭包return result
}println fab(5)
println fab2(5)// 求和1+2+3+...+number
def static cal(int number) {def result = 0number.times{num -> result += num }return result
}
println cal(101) // 0加到100// 字符串与闭包结合使用
String ss = "a b 2 c 2"
ss.each {print it.multiply(2) // 每个字符变2倍
}//println ss.find { String s -> s.isNumber() } // find查找符合条件的第一个
def list = ss.findAll { String s -> s.isNumber() } // 返回字符串中所有的数字
println list
println ss.any { String s -> s.isNumber() } // 返回true-字符串中有数字
println ss.every { String s -> s.isNumber() } // 字符串中每一项都是数字才返回true
println ss.collect { it -> it.toUpperCase() }
闭包的三个关键字:this 、owner、delegate
class Person {def static classClouser = {println "this: "+this // class Person 指向定义处的类或对象println "owner: ${owner}" // class Personprintln "delegate: ${delegate}" // class Person}def say() {def methodClouser = {println "this: "+this // Person@244a4ed8println "owner: ${owner}" // Person@244a4ed8println "delegate: ${delegate}" // Person@244a4ed8}methodClouser.call()// methodClouser.delegate = xxx delegate是可以修改的}
}
Person.classClouser.call()
new Person().say()class Student {String namedef pretty = {"My name is ${name}"}String toString() {pretty.call()}
}
class Teacher {String name
}
Student student = new Student()
student.name = "tom"
println student.toString()
集合操作
列表:
//def list = new ArrayList() //java的定义方式
def list = [1, 2, 3, 4, 5]
println list.class
println list.size()
def array = [1, 2, 3, 4, 5] as int[]
int[] array2 = [1, 2, 3, 4, 5]/**
* list的添加元素
*/
list.add(6)
list.leftShift(7)
list << 8
println list.toListString()
def plusList = list + 9
println plusList.toListString()
/**
* list的删除操作
*/
//list.remove(7)
list.remove((Object) 7)
//list.removeAt(7)
list.removeElement(6)
list.removeAll { return it % 2 == 0 }
println list - [6, 7]
println list.toListString()
/**
* 列表的排序
*/
def sortList = [6, -3, 9, 2, -7, 1, 5]
Comparator mc = { a, b ->a == b ? 0 :Math.abs(a) < Math.abs(b) ? -1 : 1
}
Collections.sort(sortList, mc)
//Groovy的排序方法
sortList.sort { a, b ->a == b ? 0 : Math.abs(a) < Math.abs(b) ? 1 : -1
}
println sortList
def sortStringList = ['abc', 'z', 'Hello', 'groovy', 'java']
sortStringList.sort { it -> return it.size() } // 按照字符串长度排序 不指定闭包默认按字典序
println sortStringList
/**
* 列表的查找
*/
def findList = [-3, 9, 6, 2, -7, 1, 5]
int result = findList.find { return it % 2 == 0 }
println result
def result2 = findList.findAll { return it % 2 != 0 }
println result2.toListString()
//def result = findList.any { return it % 2 != 0 }
//def result = findList.every { return it % 2 == 0 }
//println result
println findList.min { return Math.abs(it) }
println findList.max { return Math.abs(it) }
def num = findList.count { return it % 2 == 0 }
println num
Map:
//def map = new HashMap()
def colors = [red : 'ff0000',green: '00ff00',blue : '0000ff']
//索引方式
println colors['red']
println colors.red
colors.blue
//添加元素
colors.yellow = 'ffff00'
colors.complex = [a: 1, b: 2]
println colors.toMapString()
println colors.getClass() // class java.util.LinkedHashMap
/**
* Map操作详解
*/
def students = [1: [number: '0001', name: 'Bob',score : 55, sex: 'male'],2: [number: '0002', name: 'Johnny',score : 62, sex: 'female'],3: [number: '0003', name: 'Claire',score : 73, sex: 'female'],4: [number: '0004', name: 'Amy',score : 66, sex: 'male']
]//遍历Entry
students.each {student ->println "the key is ${student.key}, " +" the value is ${student.value}"
}
//带索引的遍历
students.eachWithIndex {student, index ->println "index is ${index},the key is ${student.key}, " +" the value is ${student.value}"
}
//直接遍历key-value
students.eachWithIndex { key, value, index ->println "the index is ${index},the key is ${key}, " +" the value is ${value}"
}
//Map的查找
def entry = students.find {student ->return student.value.score >= 60
}
println entrydef entrys = students.findAll {student ->return student.value.score >= 60
}
println entrysdef count = students.count { def student ->return student.value.score >= 60 && student.value.sex == 'male'
}
println countdef names = students.findAll {student ->return student.value.score >= 60
}.collect {return it.value.name
}
println names.toListString()def group = students.groupBy { def student ->return student.value.score >= 60 ? '及格' : '不及格'
}
println group.toMapString()/**
* 排序
*/
def sort = students.sort { def student1, def student2 ->Number score1 = student1.value.scoreNumber score2 = student2.value.scorereturn score1 == score2 ? 0 : score1 < score2 ? -1 : 1
}println sort.toMapString()
range范围:
def range = 1..10
println range[0]
println range.contains(10)
println range.from
println range.to//遍历
range.each {print it
}for (i in range) {print i
}def result = getGrade(75)
println resultstatic def getGrade(Number number) {def resultswitch (number) {case 0..<60:result = '不及格'breakcase 60..<70:result = '及格'breakcase 70..<80:result = '良好'breakcase 80..100:result = '优秀'breakdefault:result = ''break}return result
}
面向对象
def person1 = new Person(name: 'Qndroid', age: 26)
println person1.cry() // 注意此方法类中未定义//为类动态的添加一个属性
Person.metaClass.sex = 'male'
def person = new Person(name: 'Qndroid', age: 26)
println person.sex
person.sex = 'female'
println "the new sex is:" + person.sex//为类动态的添加方法
Person.metaClass.sexUpperCase = { -> sex.toUpperCase() }
def person2 = new Person(name: 'Qndroid', age: 26)
println person2.sexUpperCase()//为类动态的添加静态方法
Person.metaClass.static.createPerson = {String name, int age -> new Person(name: name, age: age)
}
def person3 = Person.createPerson('renzhiqiang', 26)
println person3.name + " and " + person3.age/**
* 1.groovy中默认都是public
*/
class Person implements Serializable {String nameInteger agedef increaseAge(Integer years) {this.age += years}/*** 一个方法找不到时,调用它代替*/def invokeMethod(String name, Object args) {return "the method is ${name}, the params is ${args}"}/*** 方法未定义时优先调用methodMissing 如果没有其次找invokeMethod 还没有就报错*/def methodMissing(String name, Object args) {return "the method ${name} is missing"}
}/**
* 接口中不许定义非public的方法
*/
interface Action {void eat()void drink()void play()
}// 允许有默认方法实现的抽象类
trait DefualtAction {abstract void eat()void play() {println ' i can play.'}
}
Json 解析
//def reponse = getNetworkData('http://yuexibo.top/yxbApp/course_detail.json')
//
//println reponse.data.head.name
//
//def getNetworkData(String url) {
// //发送http请求
// def connection = new URL(url).openConnection()
// connection.setRequestMethod('GET')
// connection.connect()
// def response = connection.content.text
// //将json转化为实体对象
// def jsonSluper = new JsonSlurper()
// return jsonSluper.parseText(response)
//}
import groovy.json.JsonOutput
import groovy.json.JsonSlurper// 对象序列化成json字符串
def list = [new Person(name:'John', age: 25),new Person(name:'Jerry', age: 26)]
def json = JsonOutput.toJson(list)
println json// json字符串反序列化成对象
def jsonSluper = new JsonSlurper()
def list2 = jsonSluper.parseText(json)
println list2[0].name
//jsonSluper.parseText(String text)
//jsonSluper.parse(byte[] bytes)
//jsonSluper.parse(char[] chars)
//jsonSluper.parse(Reader reader)class Person implements Serializable {String nameint age
}
也支持使用三方的如Gson解析库,只要放在libs
下面即可。
xml 解析
import groovy.xml.MarkupBuilderfinal String xml = '''<response version-api="2.0"><value><books id="1" classification="android"><book available="20" id="1"><title>疯狂Android讲义</title><author id="1">李刚</author></book><book available="14" id="2"><title>第一行代码</title><author id="2">郭林</author></book><book available="13" id="3"><title>Android开发艺术探索</title><author id="3">任玉刚</author></book><book available="5" id="4"><title>Android源码设计模式</title><author id="4">何红辉</author></book></books><books id="2" classification="web"><book available="10" id="1"><title>Vue从入门到精通</title><author id="4">李刚</author></book></books></value></response>
'''//开始解析此xml数据
def xmlSluper = new XmlSlurper()
def response = xmlSluper.parseText(xml)
//直接访问解析结果 不需要再转对象
println response.value.books[0].book[0].title.text()
println response.value.books[0].book[0].author.text()
println response.value.books[1].book[0].@availabledef list = []
response.value.books.each { books ->//下面开始对书结点进行遍历books.book.each { book ->def author = book.author.text()if (author == '李刚') {list.add(book.title.text())}}
}
println list.toListString()//深度遍历xml数据
def titles = response.depthFirst().findAll { book ->return book.author.text() == '李刚'
}
println titles.toListString()//广度遍历xml数据
def name = response.value.books.children().findAll { node ->node.name() == 'book' && node.@id == '2'
}.collect { node ->return node.title.text()
}
println name/**
* 生成xml格式数据
* <langs type='current' count='3' mainstream='true'>
<language flavor='static' version='1.5'>Java</language>
<language flavor='dynamic' version='1.6.0'>Groovy</language>
<language flavor='dynamic' version='1.9'>JavaScript</language>
</langs>
*/
def sw = new StringWriter()
def xmlBuilder = new MarkupBuilder(sw) //用来生成xml数据的核心类
// 直接显示的生成一段xml:
根结点langs创建成功
//xmlBuilder.langs(type: 'current', count: '3', mainstream: 'true') {
// //第一个language结点
// language(flavor: 'static', version: '1.5', 'Java')
// language(flavor: 'dynamic', version: '1.6', 'Groovy')
// language(flavor: 'dynamic', version: '1.9', 'JavaScript')
// language(flavor: 'dynamic', version: '1.6') {
// age('10') // 可继续通过闭包形式添加子节点
// }
//}
//println sw// 根据对象集合动态生成xml
def langs = new Langs()
xmlBuilder.langs(type: langs.type, count: langs.count, mainstream: langs.mainstream) {//遍历所有的子结点langs.languages.each { lang ->language(flavor: lang.flavor, version: lang.version, lang.value)}
}
println sw//对应xml中的langs结点
class Langs {String type = 'current'int count = 3boolean mainstream = truedef languages = [new Language(flavor: 'static', version: '1.5', value: 'Java'),new Language(flavor: 'dynamic', version: '1.3', value: 'Groovy'),new Language(flavor: 'dynamic', version: '1.6', value: 'JavaScript')]
}
//对应xml中的languang结点
class Language {String flavorString versionString value
}
文件操作
def file = new File('build.gradle') // 对应项目根目录下
//逐行读取
//file.eachLine { line ->
// println line
//}
//def result = file.readLines()
//一次性读取
def text = file.getText()
println text//读取文件部分内容
def res = file.withReader { reader ->char[] buffer = new char[100]reader.read(buffer)return buffer
}
println resdef result = copy('build.gradle', 'build222.gradle')
println result// 使用file的withxxx方法 通过闭包的形式读写文件 不需要关心流的关闭问题 内部做了处理
static def copy(String sourcePath, String destationPath) {try {//首先创建目标文件def desFile = new File(destationPath)if (!desFile.exists()) {desFile.createNewFile()}//开始copynew File(sourcePath).withReader { reader ->def lines = reader.readLines()desFile.withWriter { writer ->lines.each { line ->writer.append(line + "\r\n")}}}return true} catch (Exception e) {e.printStackTrace()}return false
}class Person implements Serializable {String nameint age
}
def person = new Person(name: 'Qndroid', age: 26)
saveObject(person, "${projectDir}\\person.data")//todo: 会报错 Person类找不到
def person2 = readObject("${projectDir}\\person.data")
//println "the name is ${person2.name} and the age is ${person2.age}"static def saveObject(Object object, String path) {try {//首先创建目标文件def desFile = new File(path)if (!desFile.exists()) {desFile.createNewFile()}desFile.withObjectOutputStream { out ->out.writeObject(object)}return true} catch (Exception ignored) {println "saveObject Exception: ${ignored}"}return false
}static def readObject(String path) {def obj = nulltry {def file = new File(path)if (file == null || !file.exists()) return null//从文件中读取对象file.withObjectInputStream { input ->obj = input.readObject()}} catch (Exception ignored) {println "readObject Exception: ${ignored}"}return obj
}
groovy 与 java 对比
-
写法上:没有 java 那么多的限制
-
功能上:对 java 已有的功能进行了极大的扩展
-
作用上:即可以编写应用,也可以编写脚本
第二部分:Gradle配置
认识Gradle工程
监听gradle生命周期
/**
* Gradle生命周期:
* 1.Initialization初始化阶段 解析整个工程中所有的Project, 构建所有的Project对应的project对象
*
* 2.Configuration配置阶段 解析所有的projects中的task, 构建好所有task的拓扑图
*
* 3.Execution执行阶段 执行具体的task及其依赖task
*//**
* 配置阶段开始前的监听回调
*/
this.beforeEvaluate {println "配置阶段开始前--->"
}
/**
* 配置阶段完成以后的监听回调
*/
this.afterEvaluate {println "配置阶段执行完毕--->"
}
/**
* gradle执行完毕后的监听回调
*/
this.gradle.buildFinished {println "执行阶段执行完毕--->"
}this.gradle.beforeProject {}
this.gradle.afterProject {}
Terminal 中执行命令./gradlew projects
可以输出工程中所有的project:
gradle是以树形结构来管理工程项目的。
Project相关的API
在根目录的build.gradle
中添加:
this.getProjects()def getProjects() {// 获取当前工程的所有projectthis.getAllprojects().eachWithIndex{ project, index ->println "getAllprojects-----------project.name: ${project.name} ------------index: ${index}"}// 获取当前工程的所有子projectthis.getSubprojects().eachWithIndex{ project, index ->println "getSubprojects-----------project.name: ${project.name} ------------index: ${index}"}
}
在app/build.gradle
中添加:
this.getParentProjectName()def getParentProjectName() {// 获取父peojectdef name = this.getParent().nameprintln "getParentProjectName: ${name}"println "getParentProjectName: ${this.getRootProject().name}"println "getRootDir: ${this.getRootDir().absolutePath}"println "getBuildDir: ${this.getBuildDir().absolutePath}"println "getProjectDir: ${this.getProjectDir().absolutePath}"
}
获取root project:(不管在哪个build.gradle中调都可以)
def getRootPro() {def name = this.getRootPsoject().name println "the root project name is: ${name}
}
在根目录的build.gradle
中可以找到具体的某个子project
,并对其进行一些配置:
/** project api讲解 */
project('app') { Project project -> apply plugin: 'com.android.application' group 'com.imooc'version '1.0.Ø-release' dependencies {...}android {...}
}
project('vuandroidadsdk') {apply plugin 'com.android.library' group 'com.imooc'version '1.0.Ø-release' dependencies {...}
}
还可以通过allprojects
这个api对所有的project
进行配置(包括当前节点工程):
allprojects {// 例如group "xxx"version "1.0.2"
}
subprojects
可以对所有子工程的project
进行配置(不包括当前节点工程):
// 不包括当前结点工程,只包括它的subproiect
subprojects {apply from: '../publishToMaven.gradle'
}
subprojects { Project project ->println "project.name------------->${project.name}"// 直接这样调用不能判断,必须写在afterEvaluate中才可以正确判断// println "project.plugins====${project.plugins.hasPlugin('com.android.library')}"// 只能通过这种方式判断是application还是libraryproject.afterEvaluate {println "is library: " + plugins.hasPlugin('com.android.library')plugins.withId('com.android.application') {println("-------------> module application")}plugins.withId('com.android.library') {println("-------------> module library")}plugins.withId('java') {println("-------------> module pure java")}}// project.apply from: '../publishToMaven.gradle' //引入一个写好的脚本
}
可以在根工程中通过ext
定义扩展属性,在子工程中可以直接引用扩展属性:
ext {compileSdkVersion = 25libAndroidDesign = 'com.android.support:design:25.0.0'
}
android {compileSdkVersion this.rootProject.compileSdkVersion buildToolsVersion "25.0.0
}
dependencies {compile fileTree(dir: 'libs', include:['*.jar']) //compile project(':yuandroidadsdk')compile this.rootProject.libAndroidDesign
}
还可以将版本信息统一定义到一个单独的gradle
文件中,然后在根工程中通过apply
引入该文件:
在子工程中就可以使用上面定义的版本了:
在gradle.properties
中定义变量,然后在settings.gradle
中读取变量值,根据该变量决定是否include
某个子工程:
// gradle.properties
isLoadTest=false
// settings.gradle
include ':app', ':vuandroidadsdk'
include ':lib_pullalive'
if (hasProperty('isLoadTest') ? isLoadTest.toBoolean() : false) { include ':Test'
}
文件相关的API
获取根目录:
println "getRootDir: " + getRootDir().absolutePath
println "getBuildDir: " + getBuildDir().absolutePath
println "getProjectDir: " + getProjectDir().absolutePath
读取文件:
println getContent("settings.gradle")// 读取文件内容
def getContent(String path) {try{def file = file(path) // 相对于当前目录定位文件, 如果使用files()则会返回多个文件return file.text}catch(GradleException e){println e.toString()}return null
}
拷贝文件:
//copy {
// from file("settings.gradle")
// into getRootProject().getBuildDir()
//}
copy {from file("app/build/outputs/apk")into getRootProject().getBuildDir().path + "/apk/"// rename {} // 重命名// exclude {} // 排除不想要/不需要的文件
}//对文件树进行遍历
fileTree("app/build/outputs/apk/debug") {fileTree ->fileTree.visit { element ->println "element.file.name = ${element.file.name}"copy {from element.fileinto getRootProject().getBuildDir().path + "/test/"}}
}
依赖配置
根build.gradle
中的依赖配置:
buildscript { ScriptHandler scriptHandler -> // 配置我们工程的仓库地址scriptHandler.repositories { RepositoryHandler repositoryHandler -> repositoryHandler.jcenter()repositoryHandler.mavenCentral() repositoryHandler.mavenLocal() repositoryHandler.ivy {} repositoryHandler.maven { name 'personal'url 'http://localhost:8081:/nexus/repositories/' credentials {username = 'amdin' password = 'admin123' }}}
}
buildscript {// 配置我们工程的仓库地址repositories {...}// 配置我们工程的"插件"依赖地址dependencies {classpath 'com.android.tools.build:gradle:2.2.2'classpath 'com.tencent.tinker-patch-gradle-plugin:1.7.7'}
}
出现版本冲突时exclude
排除依赖:(子工程build.gradle
中)
compile(rootProject.ext.dependence.libAutoScrollViewPager) { exclude module:'support-v4' // 排除依赖transitive false // 禁止传递依赖
}
compile(rootProject.ext.dependence.libTinker) { changing = true // 每次都从服务端拉取
}
Task相关的API
exec
执行外部命令:
task(name: 'apkcopy') {doLast {// gradle的执行阶段去执行def sourcePath = this.buildDir.path + '/outputs/apk' def desationPath = '/Users/renzhiqiang/Downloads/' def command = "mv -f ${sourcePath} ${desationPath}" exec {try {executable 'bash' args '-c', command prtinln 'the command is execute success.' }catch (GradleException e) {println 'the command is execute failed.' }}}
}
Task的创建:
// 通过task函数创建
task helloTask(group: 'myTasks', description: 'task study') {println "-----------------------> i am helloTask"
}
// 通过TaskContainer创建Task
this.tasks.create(name: 'helloTask2') {setGroup('myTasks')setDescription('task study')println "-----------------------> i am helloTask2"
}
第一种task的定义实际上是调用了一个task函数:
Task task(String name, Closure configureClosure);
task(helloTask {println 'i am helloTask.'
})
group
的作用:相同group
的task
会被放在一起,在 Android Studio 的右侧gradle
面板中可以看到对应的分组
description
的作用:注释说明
task中可以定义的属性:
注意,task里面直接写println
打印的东西会在配置阶段按顺序直接输出,写在doFirst
或doLast
里面的才会在执行阶段执行 。
task helloTask(group: 'myTasks', description: 'task study') {println "-----------------------> i am helloTask"doFirst {println "-----------------------> task group is " + group}
}
helloTask.doFirst { // 这样写执行时机会先于上面那样写println "-----------------------> task description is " + description
}
使用task计算build执行时间:
def startBuildTime, endBuildTime
// 放在afterEvaluate保证在配置阶段完成以后执行,确保能找到对应的task
this.afterEvaluate { project ->def preBuildTask = project.tasks.getByName('preBuild') // Gradle第一个执行的task是preBuildpreBuildTask.doFirst {startBuildTime = System.currentTimeMillis()println "------>the startBuildTime: ${startBuildTime}"}def buildTask = project.tasks.getByName('build') // Gradle最后一个执行的task是buildbuildTask.doLast {endBuildTime = System.currentTimeMillis()println "------>the build cost Time: ${endBuildTime - startBuildTime}"}
}
Task 的执行顺序
dependsOn
定义Task依赖执行顺序
task taskA(group: 'myTasks') {doLast {println "---------------> taskA 执行"}
}
task taskB(group: 'myTasks') {// dependsOn(taskA) // 单独执行taskB也会先执行taskAdoLast {println "---------------> taskB 执行"}
}
// taskC在执行阶段会先执行taskA和taskB
task taskC(group: 'myTasks', dependsOn: [taskA, taskB]) {doLast {println "---------------> taskC 执行"}
}
task helloworld() {10.times { i ->tasks.register("task$i") {doLast {println "Hello from task}}}tasks.named("task1") { dependson "task4", "task6", "task8" }dependson "task1"
}
定义一个 copyApk 任务并指定在默认构建任务assembleDebug
之后执行:
task copyApk(type: Copy, dependsOn: "test") {def sourceDir = layout.buildDirectory.dir("intermediates/apk/debug/app-debug.apk") def destDir = "$rootDir/apk"from sourceDir into destDirrename "app-debug.apk", "gradle-experiment.apk" doLast {def file = new File(destDir, "gradle-experiment.apk") ant.checksum file: file.path}
}tasks.whenTaskAdded { task ->if (task.name == "assembleDebug") { task.finalizedBy "copyApk" }
}
查找并依赖以某些名字开头的task任务(如lib库):
task lib1(group: 'myTasks') {doLast {println "--------------->lib1"}
}
task lib2(group: 'myTasks') {doLast {println "--------------->lib2"}
}
// 动态查找依赖task 这里还发现一点需要注意的:依赖的task必须定义在使用的地方前面,否则找不到
task taskApp(group: 'myTasks') {// 假设taskApp需要依赖所有以lib开头的library module先执行dependsOn this.tasks.findAll {task ->return task.name.startsWith("lib")}doLast {println "---------------> taskApp 执行"}
}
实例:解析一个release.xml
文件并写入到一个独立的文件中
release.xml文件内容:
<releases><release><versionCode>100</versionCode><versionName>1.0.0</versionName><versionInfo>App的第1个版本,上线了一些最基础核心的功能.</versionInfo></release><release><versionCode>101</versionCode><versionName>1.1.0</versionName><versionInfo>App的第2个版本,上线了一些最基础核心的功能.</versionInfo></release><release><versionCode>102</versionCode><versionName>1.2.0</versionName><versionInfo>App的第3个版本,上线了一些最基础核心的功能.</versionInfo></release>
</releases>
定义解析Task任务:
// 解析xml文件内容并写入一个独立文件中
task handleReleaseFile {setGroup('myTasks_handleRelease')def srcFile = file('releases.xml')def destDir = new File(this.buildDir, 'generated/release/')doLast {println "--------------->开始解析releases.xml文件"destDir.mkdirs()def releases = new XmlParser().parse(srcFile)releases.release.each { releaseNode ->// 解析每个节点内容def versionName = releaseNode.versionName.text()def versionCode = releaseNode.versionCode.text()def versionInfo = releaseNode.versionInfo.text()// 创建文件并写入节点数据def destFile = new File(destDir, "release-${versionName}.txt")destFile.withWriter{writer ->writer.write("${versionName}--${versionCode}--${versionInfo}")}}}
}
// 测试任务 依赖上面的任务
task handleReleaseFileTest(dependsOn: 'handleReleaseFile') {setGroup('myTasks_handleRelease')def dir = fileTree(this.buildDir.path + '/generated/release/')doLast {dir.each{ name -> println "the file name is ${name}" }println "--------------->输出完成"}
}
在Terminal中输入执行命令./gradlew handleReleaseFileTest
可以查看结果。
通过文件输入输出指定Task之间的依赖关系
依赖规则:如果上一个Task的输出文件是下一个Task的输入文件,则两个Task会自动建立依赖关系。
实例:创建一个Task将版本信息写入一个release.xml
文件中,并创建一个Task读取其中的信息
import groovy.xml.MarkupBuilder
/**
* 描述:版本发布文档自动维护脚本
* 流程描述: 1、请求本次版本相关信息
* 2、将版本相关信息解析出来
* 3、将解析出的数据生成xml格式数据
* 4、写入到已有的文档数据中
**/
ext {versionName = "1.2.0"// rootProject.ext.android.versionNameversionCode = 102 // rootProject.ext.android.versionCodeversionInfo = 'App的第3个版本,上线了一些最基础核心的功能.' // 实际可通过接口请求获取destFile = file('releases.xml')if (destFile != null && !destFile.exists()) {destFile.createNewFile()}
}
class VersionMsg {String versionCodeString versionNameString versionInfo
}
// 将inputs输入内容写入到outputs中
task writeTask {setGroup('myTasks_handleRelease')// 可以设置在每次执行build之后进行写入最新的版本配置信息// def buildTask = project.tasks.getByName('build')// dependsOn(buildTask)// 为task指定输入inputs.property('versionCode', this.versionCode)inputs.property('versionName', this.versionName)inputs.property('versionInfo', this.versionInfo)// 为task指定输出outputs.file this.destFiledoLast {println "writeTask------------>begin"//将输入的内容写入到输出文件中去def data = inputs.getProperties() // 会返回一个Map<String, Object>对象File file = outputs.getFiles().getSingleFile()//将map转换为实体对象def versionMsg = new VersionMsg(data)//将实体对象转换成xml数据写入到文件中def sw = new StringWriter()def xmlBuilder = new MarkupBuilder(sw)if (file.text != null && file.text.size() <= 0) {//没有内容xmlBuilder.releases {release {versionCode(versionMsg.versionCode)versionName(versionMsg.versionName)versionInfo(versionMsg.versionInfo)}}//直接写入file.withWriter { writer -> writer.append(sw.toString()) }} else {//已有其它版本内容xmlBuilder.release {versionCode(versionMsg.versionCode)versionName(versionMsg.versionName)versionInfo(versionMsg.versionInfo)}//插入到最后一行前面def lines = file.readLines()def lengths = lines.size() - 1file.withWriter { writer ->lines.eachWithIndex { line, index ->if (index != lengths) {writer.append(line + '\r\n') // 原有的内容直接append} else if (index == lengths) { // 从最后一行开始拼接新添加的内容writer.append('\r\n' + sw.toString() + '\r\n') writer.append(lines.get(lengths))}}}}println "writeTask------------>end"}
}
// 读取inputs文件内容
task readTask {setGroup('myTasks_handleRelease')mustRunAfter writeTask//指定输入文件为上一个task的输出inputs.file this.destFiledoLast {//读取输入文件的内容并显示def file = inputs.files.singleFileprintln "readTask------------>\n"+file.text}
}
// 测试任务,依赖上面两个任务
task taskTestInpusOuts {setGroup('myTasks_handleRelease')dependsOn writeTask, readTaskdoLast {println '输入输出任务结束'}
}
可以将以上任务写到一个单独的gradle
文件中,然后在app/build.gradle
中通过apply from: 'releaseinfo.gradle'
引入。
将自定义任务挂接到 Gradle 构建过程中
通过doLast
来指定:
this.project.afterEvaluate { project ->def buildTask = project.tasks.getByName('build')if (buildTask == null) {throw GradleException("the build task is not found")}buildTask.doLast{println "--------------->afterEvaluate"writeTask.execute()}
}
通过dependsOn
来指定:
// 设置taskD在build任务之后执行
task taskD(group: 'myTasks') {def buildTask = project.tasks.getByName('build')dependsOn(buildTask)doLast {println "---------------> taskD 执行"}
}
通过mustRunAfter
来指定:
task taskB(group: 'myTasks') {// 如果单独执行taskB不会先执行taskA,只有taskB与taskA一起执行时才有效mustRunAfter(taskA)doLast {println "---------------> taskB 执行"}
}// 另外一种写法
taskB.mustRunAfter(taskA)
Tinker 就是通过mustRunAfter
和dependsOn
将自定义的Task挂接到系统的构建任务之中的:
Task 的类型
Gradle提供了很多任务类型,具体可看:
- https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html
- https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Delete.html (等等,在左边栏有分类)
task copyDocs(type: Copy) {from 'src/main/doc'into 'build/target/doc'
}
task makePretty(type: Delete) {delete 'uglyFolder', 'uglyFile'followSymlinks = true
}
获取并打印所有变体的信息
this.afterEvaluate {this.android.applicationVariants.all { variant ->println "variant.name--------------->${variant.name}"println "variant.baseName----------->${variant.baseName}"println "variant.versionCode-------->${variant.versionCode}"println "variant.versionName-------->${variant.versionName}"println "variant.flavorName--------->${variant.flavorName}"println "variant.buildType---------->${variant.buildType.name}"println "variant.description-------->${variant.description}"println "variant.assemble----------->${variant.assemble.name}"println "variant.checkManifest----------->${variant.checkManifest.name}"def checkTask = variant.checkManifestcheckTask.doFirst {println "---------------> checkTask.doFirst"if (variant.buildType.name == 'release') {//update_plugin() // 查看参考packageplugin.gradle 主要是json解析和文件下载}}}
}
修改apk的名称
android { ...productFlavors {huawei {dimension "default"}xiaomi {dimension "default"}}task changeApkName() {applicationVariants.all { variant ->println "variant.name--------------->${variant.name}"println "variant.baseName----------->${variant.baseName}"println "variant.versionCode-------->${variant.versionCode}"println "variant.versionName-------->${variant.versionName}"println "variant.flavorName--------->${variant.flavorName}"println "variant.buildType---------->${variant.buildType.name}"println "variant.description-------->${variant.description}"println "variant.assemble----------->${variant.assemble.name}"println "variant.checkManifest----------->${variant.checkManifest.name}"//println "variant.signingConfig------>${variant.signingConfig.name}" // null//def output = variant.outputs.first()def apkName = "app-${variant.baseName}-${new Date().format('yyyyMMdd')}-${variant.versionName}.apk"variant.outputs.all { output ->outputFileName = apkName}println "\n"}}
}
android{} 闭包中的配置项
android
闭包中可以配置的选项可以查看BaseExtension
类
通过 sourceSets
修改源文件的默认存放位置:
例如可以修改so
文件的默认存放位置、为res
文件夹添加分包等
可以修改哪些内容具体可以查看 AndroidSourceSet
类中哪些可以配置的。
自定义plugin插件
在根目录建立一个名称为buildSrc
的工程
然后在其中的groovy
文件夹下面建立groovy类编写代码即可
然后在resources
文件夹下指定该类:
// 位置在 resources/com.imooc.gradle.study.properties
implementation-class=com.imooc.gradle.study.GradleStudyPlugin
在工程中引入应用插件:
apply plugin:'com.imooc.gradle.study'
在自定义的Task
类中的被@TaskAction
注解的doAction
方法中可以读取在build.gradle
中为插件传入的参数信息,然后执行具体的任务(例如将读取的参数信息写入到xml文件中,代码将前面的写入xml内容的部分拷贝过来即可)。如下:
/**
* 为自定义插件传递参数
*/
imoocReleaseInfo {versionCode = rootProject.ext.android.versionCode versionName = rootProject.ext.android.versionName versionInfo = '第8个版本...'fileName = 'releases.xml'
}
doFirst
和doLast
方法分别会在被@TaskAction
注解的doAction
方法的前后执行。
在项目中引入自定义插件后,Android Studio 的 gradle
面板中也会显示对应的task
任务名称: