查看原文:http://blog.csdn.net/u010818425/article/details/52268126html
本文篇幅較長,文中系統地講解了Gradle的基本知識點以及一些經常使用的命令和配置,適合於剛接觸Gradle的同窗;java
在此基礎上,能夠閱讀如下的實戰經驗文章:
《Gradle實戰:Android多渠道打包方案彙總》
《Gradle實戰:不一樣編譯類型的包同設備共存》
《Gradle實戰:發佈aar包到maven倉庫》
《Gradle實戰:執行sql操做hive數據庫》android
Domain Specific Language,領域相關語言
Gradle是一個框架,它負責定義流程和規則; 每個待編譯的工程都叫一個Project; 每個Project在構建的時候都包含一系列的Task。
Closure是一段單獨的代碼塊,它能夠接收參數,返回值,也能夠被賦值給變量:sql
//無參數 def Closure1 = { println 'Hello world' } Closure1() // 執行閉包,輸出Hello world //接收一個參數 def Closure2 = { String str -> println str //箭頭前面是參數定義,後面是執行代碼,str爲外部傳入的參數 } //若是隻有一個參數能夠用it代替,也可寫做: def Closure2 = { println it } Closure2('Hello world')// 執行閉包,輸出Hello world //接收多個參數 def Closure3 = { String str , int n -> println "$str : $n" //參數前加$ } //也能夠寫做: def Closure3 = { str , n -> println "$str : $n" } Closure3('Hello world’, 1) // 執行閉包,輸出Hello world : 1 //使用變量 def var = "Hello world" def Closure4 = { println var } Closure4() // 執行閉包,輸出Hello world //改變上下文 def Closure5 = { println Var //這時還不存在 } MyClass m = new MyClass() Closure5.setDelegate(m) // 改變上下文,這時Var已經有了,在執行以前改變了 Closure5() //執行閉包,輸出Hello world class MyClass { def Var = 'Hello world' }
Gradle在默認狀況下已經爲Project定義了不少Property,以下:shell
project:Project自己 name:Project的名字 path:Project的絕對路徑 description:Project的描述信息 buildDir:Project構建結果存放目錄 version:Project的版本號
經過ext來自定義Property:數據庫
ext.property1 = "this is property1" 或 ext { property2 = "this is property2" } task showProperties << { println property1 //直接訪問 println property2 }
任何實現了ExtensionAware接口的Gradle對象均可以經過這種方式來添加額外的Property,好比Task也實現了該接口。json
經過「-p」命令行參數定義Property:api
task showCommandLineProperties << { println propertyTest } gradle -P propertyTest ="this is propertyTest" showCommandLineProperties
經過JVM系統參數定義Property(須要以「org.gradle.project」爲前綴):服務器
gradle -D org.gradle.project.propertyTest="this is another propertyTest" showCommandLineProperties 另外一種方式: 寫入參數:gradle -DpropertyTest="this is another propertyTest" 讀取參數:def propertyTest = System.properties['propertyTest']
經過環境變量設置Property(須要以「ORG_ GRADLE_ PROJECT_ 」爲前綴):閉包
export ORG_GRADLE_PROJECT_propertyTest = "this is yet another propertyTest" gradle showCommandLineProperties
> Groovy會爲每個字段自動生成getter和setter,咱們能夠經過像訪問字段自己同樣調用getter和setter,如: class GroovyBeanExample { private String name } def bean = new GroovyBeanExample() bean.name = 'this is name' //Groovy動態地爲name建立了getter和setter println bean.name
delegate機制可使咱們將一個閉包中的執行代碼的做用對象設置成任意其餘對象
class Child { private String name } class Parent { Child child = new Child(); void configChild(Closure c) { c.delegate = child c.setResolveStrategy Closure.DELEGATE_FIRST //默認狀況下是OWNER_FIRST,即它會先查找閉包的owner(這裏即parent) c() } } def parent = new Parent() parent.configChild { name = "child name" } println parent.child.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
tasks.create(name: 'hello') << { println 'hello' }
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 } hello3.configure { description = "this is hello3" }
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' } tests.dependsOn unit //單元測試 tests.dependsOn ui //ui測試 ui.mustRunAfter unit //ui測試必須在單元測試以後執行 tests.finalizedBy mergeReports //表示tests執行完後,再執行mergeReports,等價於mergeReports.dependsOn tests
爲一個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」需配置環境變量
清除build文件夾
./gradlew clean
檢查依賴並編譯打包
./gradlew build
編譯並打Debug包
./gradlew assembleDebug
編譯並打Release包
./gradlew assembleRelease
獲取gradle版本號
./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文件所在的目錄
執行shell命令
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 //打印系統日期 }
執行java代碼
task runJava(type: JavaExec) { classpath = sourceSets.main.runtimeClasspath //執行文件所在路徑 main = 'com.example.MyClass' // 執行方法所在類 // arguments to pass to the application args 'haha','xixi' //多個參數用逗號隔開 }
生成jar包
方法一: 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' }
本地aar包依賴
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/' ] } }
~/.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