本文篇幅较长,文中系统地讲解了Gradle的基本知识点以及一些经常使用的命令和配置,适合于刚接触Gradle的同窗;html
在此基础上,能够阅读如下的实战经验文章:java
《Gradle实战:Android批量打包》
《Gradle实战:不一样编译类型的包同设备共存》
《Gradle实战:发布aar包到maven仓库》
《Gradle实战:执行sql操做hive数据库》android
Domain Specific Language,领域相关语言。
Gradle是一个框架,它负责定义流程和规则; 每个待编译的工程都叫一个Project; 每个Project在构建的时候都包含一系列的Task。
Closure(闭包)是一段单独的代码块,它能够接收参数,返回值,也能够被赋值给变量:sql
Gradle在默认状况下已经为Project定义了不少Property,以下:shell
project:Project自己数据库
name:Project的名字json
path:Project的绝对路径api
description:Project的描述信息服务器
buildDir:Project构建结果存放目录闭包
version:Project的版本号
ext.property1 = "this is property1" 或 ext { property2 = "this is property2" } task showProperties << { println property1 //直接访问 println property2 }
任何实现了ExtensionAware接口的Gradle对象均可以经过这种方式来添加额外的Property,好比Task也实现了该接口。
task showCommandLineProperties << { println propertyTest } gradle -P propertyTest ="this is propertyTest" showCommandLineProperties
gradle -D org.gradle.project.propertyTest="this is another propertyTest" showCommandLineProperties 另外一种方式: 写入参数:gradle -DpropertyTest="this is another propertyTest" 读取参数:def propertyTest = System.properties['propertyTest']
export ORG_GRADLE_PROJECT_propertyTest = "this is yet another propertyTest" gradle showCommandLineProperties
Groovy会为每个字段自动生成getter和setter,咱们能够经过像访问字段自己同样调用getter和setter,如:
class GroovyBeanExample { private String name }
delegate机制可使咱们将一个闭包中的执行代码的做用对象设置成任意其余对象
class Child { private String name }
task有两个生命周期,配置阶段和执行阶段。
gradle在执行task时,都会先对task进行配置,task中最顶层的代码就是配置代码,在配置阶段执行,其余代码实在执行阶段执行的; task关键字其实是一个方法调用,咱们不用将参数放在括号里面。 task Task1 { println "hello” // 这段代码是在配置阶段执行的 } task Task2 { def name = "hello” // 这段代码是在配置阶段执行的 doLast { println name } // 这段代码是在执行阶段执行的,至关于: // doLast({ // println 'Hello world!' // }) } task Task3 << { println name } // “<<”语法糖,表示追加执行过程,至关于doLast,所以整个代码都是在执行阶段执行的;与之相反的是doFirst。 //若是代码没有加“<<”,则这个任务在脚本initialization的时候执行(也就是你不管执行什么任务,这个任务都会被执行,“hello”都会被输出); //若是加了“<<”,则在输入命令gradle Task3后才执行经过TaskContainer的create()方法建立Task
class HelloWorldTask extends DefaultTask { @Optional String message = 'I am davenkin' @TaskAction def hello(){ println "hello world $message" } } task hello(type:HelloWorldTask) task hello1(type:HelloWorldTask){ message ="I am a programmer" }
@TaskAction表示该Task要执行的动做,@Optional表示在配置该Task时,message是可选的
方法一:在定义Task的时候对Property进行配置
task hello1 << { description = "this is hello1" println description }方法二:经过闭包的方式来配置一个已有的Task
task hello2 << { println description } hello2 { description = "this is hello2" } //Gradle会为每个task建立一个同名的方法,该方法接受一个闭包 或 hello2.description = "this is hello2"//Gradle会为每个task建立一个同名的Property,因此能够将该Task看成Property来访问注:对hello2的description的设置发生在定义该Task以后,在执行gradle hello2时,命令行依然能够打印出正确的“this is hello2”,这是由于Gradle在执行Task时分为两个阶段:配置阶段、执行阶段。
因此在执行hello2以前,Gradle会扫描整个build.gradle文档,将hello2的description设置为“this is hello2”,再执行hello2。方法三:经过Task的configure()方法完成Property的设置
task hello3 << { println description }
task A << { println 'Hello from A' } task B << { println 'Hello from B' } B.dependsOn A 或 task A << { println 'Hello from A' } task B { dependsOn A doLast { println 'Hello from B' } } 或 task B(dependsOn: A) { println 'Hello from B' }
指之间无依赖关系的任务
task unit << { println 'Hello from unit tests' } task ui << { println 'Hello from UI tests' } task tests << { println 'Hello from all tests!' } task mergeReports << { println 'Merging test reports' }
为一个Task定义输入(inputs)和输出(outputs),在执行该Task时,若是它的输入和输出与前一次执行时没有变化,那么Gradle便会认为该Task是最新的(日志会输出“UP-TO-DATE“),所以不会重复执行
task combineFileContent { def sources = fileTree('sourceDir') def destination = file('destination.txt') inputs.dir sources // 将sources声明为该Task的inputs outputs.file destination // 将destination声明为outputs doLast { destination.withPrintWriter { writer -> sources.each {source -> writer.println source.text } } } }当首次执行combineFileContent时,Gradle会完整地执行该Task,可是紧接着再执行一次,命令行显示:
:combineFileContent UP-TO-DATE //被标记为UP-TO-DATE,表示该Task是最新的,不执行 BUILD SUCCESSFUL Total time: 2.104 secs若是修改inputs(上述即sourceDir文件夹)中的任何一个文件或删除destination.txt,再次调用“gradle combineFileContent”时,该Task又会从新执行
在当前工程中的buildSrc/src/main/groovy/davenkin目录下建立DateAndTimePlugin.groovy文件和DateAndTimePluginExtension.groovy文件
// DateAndTimePlugin.groovy package com.gradle.test import org.gradle.api.Plugin import org.gradle.api.Project class DateAndTimePlugin implements Plugin<Project> { void apply(Project project) { //每一个Gradle的Project都维护了一个ExtenionContainer,咱们能够经过project.extentions访问额外的Property和定义额外的Property project.extensions.create("dateAndTime", DateAndTimePluginExtension) project.task('showTime') << { println "Current time is " + new Date().format(project.dateAndTime.timeFormat) } project.tasks.create('showDate') << { println "Current date is " + new Date().format(project.dateAndTime.dateFormat) } } } // DateAndTimePlugin.groovy package com.gradle.test class DateAndTimePluginExtension { String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS" String dateFormat = "yyyy-MM-dd" }
build.gradle文件中,再apply该Plugin
apply plugin: com.gradle.test.DateAndTimePlugin // 能够经过如下方式对这两个Property进行从新配置 dateAndTime { timeFormat = 'HH:mm:ss.SSS' dateFormat = 'MM/dd/yyyy' }
注:执行“./gradlew xxx”等同于执行“gradle xxx”,但执行“gradle xxx”需配置环境变量
./gradlew clean
./gradlew build
./gradlew assembleDebug
./gradlew assembleRelease
./gradlew -v
./gradlew tasks 或 gradle tasks
gradle projects
gradle properties
task A << { println 'Hello from A' } 终端输入:gradle A
task copyFile(type: Copy) { from 'source' into 'destination' } 将source文件夹中的全部内容拷贝到destination文件夹中, 这两个文件夹都是相对于当前Project而言的,即build.gradle文件所在的目录
task deleteTest(type: Delete) { delete 'file' ,'dir' } 文件和文件夹是相对于当前Project而言的,即build.gradle文件所在的目录
task runShell1(type: Exec) { executable "sh" args "-c", "rm ./app/libs/test.jar" //路径是相对于当前build.gradle文件 } 或者 def cmd = 'date +%Y-%m-%d' task shellTest << { String date = cmd.execute().text.trim().toString() //带返回值 print date //打印系统日期 }
task runJava(type: JavaExec) { classpath = sourceSets.main.runtimeClasspath //执行文件所在路径 main = 'com.example.MyClass' // 执行方法所在类 // arguments to pass to the application args 'haha','xixi' //多个参数用逗号隔开 }
方法一: task deleteDes(type: Delete) { delete 'build/intermediates/bundles/release/class.jar', 'libs/mylib.jar' } task makeJar(type: Copy) { from('build/intermediates/bundles/release/') { include '*.jar' // 只拷贝jar格式的文件 } into('libs') //include('classes.jar') // 只拷贝classes.jar这个文件 rename('classes.jar', 'mylib.jar') // 重命名为mylib.jar } task releaseLib(type: Copy, dependsOn: [deleteDes, build, makeJar]) { // 依赖多个任务 into "../app/libs" from 'libs' } // 手动打包,gradle releaseLib 方法二: // 如下是在编译流程中插入生成jar包的task android.libraryVariants.all { variant -> variant.outputs.each { output -> def file = output.outputFile def fileName = 'classes.jar' def name = variant.buildType.name task "makeJar${variant.name.capitalize()}" << { copy { from("${projectDir}/build/intermediates/bundles/"+"${name}") { include(fileName) } into(file.parent) // 可自定义存放路径 rename (fileName, "${project.name}"+"-${name}.jar") } } } } project.tasks.whenTaskAdded { task -> android.libraryVariants.all { variant -> if (task.name == "bundle${variant.name.capitalize()}") { task.finalizedBy "makeJar${variant.name.capitalize()}" } } }
//以读取properties文件中保存的签名信息为例 def File propFile = new File('signing.properties') if (propFile.canRead()) { def Properties props = new Properties() props.load(new FileInputStream(propFile)) if (props != null && props.containsKey('RELEASE_STORE_FILE') && props.containsKey('RELEASE_STORE_PASSWORD') && props.containsKey('RELEASE_KEY_ALIAS') && props.containsKey('RELEASE_KEY_PASSWORD')) { android.signingConfigs.release.storeFile = file(props['RELEASE_STORE_FILE']) android.signingConfigs.release.storePassword = props['RELEASE_STORE_PASSWORD'] android.signingConfigs.release.keyAlias = props['RELEASE_KEY_ALIAS'] android.signingConfigs.release.keyPassword = props['RELEASE_KEY_PASSWORD'] println 'all good to go' } else { android.buildTypes.release.signingConfig = null println 'signing.properties found but some entries are missing' } } else { println 'signing.properties not found' android.buildTypes.release.signingConfig = null }
ext { compileSdkVersion = 22 buildToolsVersion = "22.0.1" } 在module中引用全局参数: android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion }
将属性或方法放入ext{}就能够被全局引用
allprojects { tasks.withType(JavaCompile) { options.encoding = "UTF-8" } }
allprojects { tasks.withType(JavaCompile) { sourceCompatibility = JavaVersion.VERSION_1_7 targetCompatibility = JavaVersion.VERSION_1_7 } }
compile 'com.alibaba.fastjson.latest.integration' { //latest.integration 获取服务器上最新版本 exclude module: 'annotations', group: 'com.google.android' }
allprojects { repositories { jcenter() flatDir { dirs 'libs' } } } dependencies { compile(name:'本地库aar的名字,不带后缀', ext:'aar') }
Gradle的每一个source set都包含有一个名字,而且包含有一个名为java的Property和一个名为resources的Property,他们分别用于表示该source set所包含的Java源文件集合和资源文件集合。在实际应用时,咱们能够将他们设置成任何目录值,如下将资源文件
按功能分包
,使得与java文件分包保持一致sourceSets { main { manifest.srcFile 'src/main/AndroidManifest.xml' java.srcDirs = ['src/main/java','.apt_generated'] aidl.srcDirs = ['src/main/aidl','.apt_generated'] assets.srcDirs = ['src/main/assets'] res.srcDirs = [ 'src/main/res/bindmobile', //绑定手机资源 'src/main/res/bycaptcha', //验证码登陆资源 'src/main/res/bypwd', //密码登陆资源 'src/main/res/pwdmodify', //密码修改资源 'src/main/res/pwdreset', //密码重置资源 'src/main/res/resource', //其余资源 'src/main/res/' ] } }
cd ~/.gradle/gradle.properties //添加以下配置(没有该文件则新建一个): org.gradle.daemon=true //独立进程,中止后台进程命令:gradle --stop org.gradle.parallel=true //并行构建,须要将项目拆分红多个子项目,经过aar引用才能起效 org.gradle.configureondemand=true //按需配置,目前还在试验孵化阶段,默认是关闭的
打开settings->Build,Execution,Deployment->Build Tools->Gradle, 选中Offlie Work //更新依赖包时要取消它
在命令后面加上以下参数
--daemon --parallel --offline --dry-run