若是是你想幹的事情,在別人看起來多是很難的一件事,不過你本身很喜歡,你不會以爲很苦。我開始創業那會是28歲。對我來說,我創業的目的不是爲了本身當老闆,我但願有一個平臺有一個環境,我能夠控制一些資源,讓我去創造一個新的產品和服務;
—— 周鴻禕php
Gradle是一種依賴管理工具,基於Groovy語言,面向Java應用爲主,它拋棄了基於XML的各類繁瑣配置,取而代之的是一種基於Groovy的領域特定(DSL)語言。css
固然,咱們如今最多都是在Android Studio的項目中,和我同樣沒有接觸過的就當看看個人學習筆記吧。java
隨便找到了一個Android Sdk中的一個Sample代碼,看了一下它的gradle腳本。以下所示:android
apply plugin: 'com.android.application' android { compileSdkVersion 21 #build的sdk版本 buildToolsVersion "21.1.2" #build的工具版本 defaultConfig { #默認配置,versionname,versioncode等 applicationId "com.example.hello" minSdkVersion 15 targetSdkVersion 21 versionCode 1 versionName "1.0" } buildTypes { #編譯方式 release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { #第三方依賴庫 compile fileTree(include: ['*.jar'], dir: 'libs') compile 'com.android.support:appcompat-v7:21.0.3' }
咱們從閱讀上看來是很簡單的,咱們徹底沒有學習過gradle也可以看到它的腳本大概的意思。那麼,咱們該如何書寫此類腳本能?這類的腳本又可以幫助咱們作一些什麼複雜的事情?數組
因爲在網上也沒有找到一個比較全面的Android Gradle教程,因此,本身根據官方的入門文檔:
http://tools.android.com/tech-docs/new-build-system/user-guide安全
翻譯文以下:ruby
簡單的構建文件,最簡單的 純Java項目的build.gradle以下所示:bash
apply plugin: 'java'
這裏配置使用了Gradle內置的 Java 插件。該插件提供用於構建並測試 Java 應用程序所須要的東西。服務器
最簡單的 Android 項目的 build.gradle 則是如下內容:閉包
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.11.1' } } apply plugin: 'android' android { compileSdkVersion 19 buildToolsVersion "19.0.0" }
在這個 Android 構建文件中,有3個主要部分:
buildscript: 配置了驅動構建的代碼。
apply plugin: android插件,配置構建Android須要的東西。
android:配置了用於 android 構建的全部參數。
Tips:你應該只配置使用這個android插件。若是同時配置使用了java插件也會致使構建錯誤。
基本項目開始於兩個名爲「source sets」的組件。即主源代碼和測試代碼。它們分別在:
裏面的每一個文件夾中都存在對應的源代碼組件的文件夾。
對於 Java 和 Android 插件,Java 源代碼和 Java 資源的位置以下:
對於Android 插件,Android所特有的額外的文件和文件夾是:
注: src/androidTest/AndroidManifest.xml是不須要的,由於它會被自動建立。
當IDE環境沒有自動的生成構建的Gradle腳本,咱們能夠根據 Gradle 文檔,爲一個Java 項目從新配置 sourceSets能夠經過以下方法實現:
sourceSets { main { java { srcDir 'src/java' } resources { srcDir 'src/resources' } } }
注: srcDir實際上會將給定的文件夾添加到現有的源文件夾列表中 (這在Gradle 文檔中沒有說起,但這是實際的行爲)。
若是要替換默認的源文件夾,您就要使用傳入一個路徑數組的srcDirs來代替。如下是使用涉及的對象的另外一種不一樣的方法:
sourceSets { main.java.srcDirs = ['src/java'] main.resources.srcDirs = ['src/resources'] }
Android 插件使用相似的語法,但由於它使用它本身的sourceSets,因此在android對象裏面來實現。
這裏有一個例子,使用舊的項目結構的主源碼並從新映射androidTest sourceSet 到tests文件夾:
android {
sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] renderscript.srcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] } androidTest.setRoot('tests') } }
在構建文件中配置使用一個插件,將自動建立一系列要運行的構建任務。Java 插件和 Android 插件約定的任務以下:
assemble,check和build這些任務,實際上不作任何事情。他們是錨記任務,用於讓插件添加實際的任務去作這些事情。這容許您可以調用一樣的任務,不管項目的類型是什麼,或者是配置使用了什麼插件。
例如,配置使用findbugs插件將建立一個新的任務和使check任務依賴於它,使得每當調用check任務時都會調用到它。
您能夠從命令行中運行下面的命令來獲取更高級別的任務:
gradle tasks
下面的命令能夠獲得一個完整的任務列表,而且看到任務運行之間的依賴關係:
gradle tasks --all
Java 插件主要建立兩個任務,它們是主要錨記任務的依賴任務:
jar任務自己會直接或間接地依賴其餘任務: 例如,classes任務用於編譯 Java 代碼。
testClasses任務用於編譯測試,但它不多會被調用,由於test任務依賴於它 (以及classes任務)。
通常狀況下,你將可能永遠只調用assemble或check,而無視其餘任務。
Android 的插件使用相同的約定配置以兼容其餘插件,並添加了另外的錨記任務:
新的錨記任務是有必要的,以便可以運行按期的檢查而無需鏈接的設備。
注意到,build任務不依賴於deviceCheck或connectedCheck。
Android 項目具備至少兩個輸出: debug版本的APK 和release版本的 APK。這裏的每個輸出都有本身的錨記任務,以便單獨構建它們:
它們兩個都依賴於執行構建一個 APK所需的多個步驟的其餘任務。assemble任務取則依賴於這兩個任務,因此調用 assemble 將會構建出這兩個 APKs。
Tips:在命令行上,Gradle 支持任務名稱的駝峯命名法的簡寫。例如: gradle aR 至關於輸入 gradle
assembleRelease 只要沒有其餘任務匹配 「aR」
check錨記任務有它們本身的依賴項:
這個任務依賴於當其餘插件實現了測試擴展點時建立的任務。
最後,該插件爲全部構建類型 (debug、release、test)建立了omstal/uninstall 任務,只要他們能夠被安裝(須要簽名)。
Android 插件提供了大量的 DSL,以經過構建系統直接地自定義大部分事情。經過 DSL 能夠配置如下清單條目:
示例:
android {
compileSdkVersion 19 buildToolsVersion "19.0.0" defaultConfig { versionCode 12 versionName "2.0" minSdkVersion 16 targetSdkVersion 16 } }
在android元素的內部的defaultConfig元素是定義全部這些配置的地方。
之前版本的 Android 插件使用packageName來配置清單的「packageName」屬性。 從 0.11.0開始,你應該在
build.gradle 中使用 applicationId 來配置清單中的「packageName」條目。 它消除了應用程序的包名(指它的
ID)和java 包名之間的所引發的混亂。
在構建文件中描述它的強大之處是它能夠是動態的。
例如,能夠從文件中的某處或使用一些自定義的邏輯讀取版本信息:
def computeVersionName() {
... } android { compileSdkVersion 19 buildToolsVersion "19.0.0" defaultConfig { versionCode 12 versionName computeVersionName() minSdkVersion 16 targetSdkVersion 16 } }
Tips: 不要使用在做用域內可能與已存在的getter函數有衝突的函數名稱。例如 defaultConfig { …} 實例調用 getVersionName() 時將自動使用 defaultConfig.getVersionName() 的 getter 方法,而不是自定義的方法。
若是一個屬性未經過 DSL 來設置,它將使用默認值。下表描述了對於未設置的屬性的處理方式。
屬性名稱 | DSL 對象中的默認值 | 默認值 |
---|---|---|
versionCode | -1 | 若是在清單中存在,則使用清單中的值 |
versionName | null | 若是在清單中存在,則使用清單中的值 |
minSdkVersion | -1 | 若是在清單中存在,則使用清單中的值 |
targetSdkVersion | -1 | 若是在清單中存在,則使用清單中的值 |
applicationId | null | 若是在清單中存在,則使用清單中的值 |
testApplicationId | null | applicationId + 「.test」 |
testInstrumentationRunner | null | |
android.test.InstrumentationTestRunner | ||
signingConfig | null | null |
proguardFile | N/A (只設置) | N/A (只設置) |
proguardFiles | N/A (只設置) | N/A (只設置) |
第二列的值是很重要的,若是您在構建腳本中使用自定義邏輯查詢這些屬性的話。例如,您能夠編寫:
if (android.defaultConfig.testInstrumentationRunner == null) { // assign a better default... }
若是值仍然爲null,那麼在構建的時候它將會被設爲第三列中的實際默認值,可是因爲 DSL 元素不包含此默認值,所以您沒法查詢它。
這是爲了防止解析應用程序的清單,除非真的很須要。
默認狀況下,Android 插件自動將項目設置爲生成應用程序的的debug和release版本。
這兩個版本的不一樣,大可能是圍繞在調試一個安全的(非開發版的)設備的能力,以及 apk 怎麼簽名。
調試版本使用自動建立的密鑰/證書籤名,而且密鑰/證書的用戶名/密碼是已知的(以防止構建過程當中須要相關的信息)的。release版本在構建的時候不會進行簽名,須要在以後進行簽名。
這個配置是經過一個叫BuildType的對象來完成的。默認狀況下,2 個實例會被建立,分別是debug版和release版。
Android 插件容許自定義這兩個實例,以及建立其餘的構建類型。它經過buildTypes DSL 容器來實現:
android {
buildTypes {
debug { applicationIdSuffix ".debug" } jnidebug.initWith(buildTypes.debug) jnidebug { packageNameSuffix ".jnidebug" jniDebuggable true } } }
上面的代碼段可實現如下操做:
建立新的 Build Types 就是簡單地在buildTypes下添加一個新的元素,而後調用 initWith()或者是使用一個閉包來配置。
如下是可能用到的屬性和它們的默認值:
屬性名稱 | debug的默認值 | release/其餘的默認值 |
---|---|---|
debuggable | true | false |
jniDebuggable | false | false |
renderscriptDebuggable | false | false |
renderscriptOptimLevel | 3 | 3 |
applicationIdSuffix | null | null |
versionNameSuffix | null | null |
signingConfig | android.signingConfigs.debug | null |
zipAlignEnabled | false | true |
minifyEnabled | false | false |
proguardFile | N/A (只設置) | N/A (只設置) |
proguardFiles | N/A (只設置) | N/A (只設置) |
除了這些屬性,Build Types還會影響到構建的代碼和資源。
對每一個Build Type都會建立一個自動匹配的sourceSet,默認位置爲
src/<buildtypename>/
這意味着Build Type的名字不能爲main或者是androidTest (這是插件所強制的),而且它們之間必須是惟一的。
與任何其餘source set同樣,生成類型的source set的位置也是能夠從新設置的:
android {
sourceSets.jnidebug.setRoot('foo/jnidebug') }
此外,對於每一個Build Type,會建立一個新的assemble任務。
已經提到過的assembleDebug和assembleRelease這兩個任務,這裏也會講一下它們是怎麼來的。當debug和releaseBuild Types被預建立的時候,他們的任務也會被自動建立。而後,
上面的build.gradle片斷也會生成一個assembleJnidebug任務,而且assemble將會依賴於它,就像它依賴於assembleDebug和assembleRelease任務同樣。
Tips: 請記住您能夠輸入gradle aJ來運行assembleJnidebug任務。
可能會用到的狀況:
BuildType的代碼和資源經過如下方式被使用:
對應用程序進行簽名,要求以下:
存儲類型
簽名文件的位置,key的名稱,以及這兩個密碼和存儲類型,一塊兒構成了一個簽名配置 ( SigningConfig類型)
默認狀況下,有一個debug的配置,配置使用了一個debug keystore。這個keystore使用了一個已知的key和一個已知的密碼。
這個debug keystore 位於$HOME/.android/debug.keystore,而且會在不存在時被建立。debug Build Type被設置爲自動使用此debug
你也能夠建立其餘配置,或者自定義某個默認的內置配置。經過signingConfigs DSL 容器來實現:
android {
signingConfigs {
debug {
storeFile file("debug.keystore") } myConfig { storeFile file("other.keystore") storePassword "android" keyAlias "androiddebugkey" keyPassword "android" } } buildTypes { foo { debuggable true jniDebuggable true signingConfig signingConfigs.myConfig } } }
上面的代碼段把debug keystore的位置修改成在項目的根位置下。這會自動影響到任何設置爲使用它的Build Types,在這裏,影響到的是debug Build Type。
代碼的代碼還建立了一個新的Signing Config和使用新配置的新的Build Type 。
Tips:只有位於默認位置下的debug keystores纔會被自動建立。若是debug
keystore的位置被更改了,它將不會在須要時自動建立。建立一個使用一個不一樣的名稱SigningConfig,但使用了默認的debug
\
Tips: keystore的路徑一般使用項目根目錄的相對路徑,但也能夠是使用絕對路徑,儘管這不推薦 (除了自動建立的debug keystore)。——–
ProGuard 是經過 Gradle plugin for ProGuard version 4.10來進行支持的。ProGuard 插件會被自動配置使用,而且若是Build Type經過minifyEnabled屬性配置爲運行ProGuard,對應的任務將被自動建立。
android { buildTypes { release { minifyEnabled true proguardFile getDefaultProguardFile('proguard-android.txt') } } productFlavors { flavor1 { } flavor2 { proguardFile 'some-other-rules.txt' } } }
變種使用他們的構建類型中所聲明的規則文件,product flavors(定製版本)則使用flavor中聲明的規則文件。
這裏有 2 個默認的規則文件
它們位於 SDK 中。使用getDefaultProguardFile()將返回的文件的完整路徑。它們除了是否啓用優化以外,其它都是相同的。
Gradle 項目能夠對其餘組件具備依賴關係。這些組件能夠是外部的二進制包,或其餘的 Gradle 項目。
本地包
要配置一個外部庫 jar 包的依賴,您須要在compile配置中添加一個依賴關係。
dependencies {
compile files('libs/foo.jar') } android { ... }
Tips:dependencies DSL 元素是標準的 Gradle API 的一部分,不屬於android 元素內。
compile配置用於編譯主應用程序。裏面的全部內容都會被添加到編譯類路徑,而且打包到最終生成的 apk 當中。下面是添加依賴時其餘可能用到的配置:
由於不可能構建一個沒有任何關聯的Build Type的 APK,apk 老是配置兩個(或以上)的配置:compile和Compile。
建立一個新的Build Type會基於它的名字自動建立一個新的配置。
這可能會有用,好比debug版本須要使用一個自定義庫(例如報告崩潰的信息),而release版本則不須要,或者是他們依賴於同一個庫的不一樣版本的狀況下。
Gradle 支持從 Maven 和 Ivy 倉庫中拉取文件。
首先,這個倉庫必須添加到列表當中,而後必須用Maven 或 Ivy 聲明文件的方式聲明這個
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.guava:guava:11.0.2' } android { ... }
mavenCentral()是指定maven中央倉庫的URL的快捷方法。Gradle支持遠程和本地倉庫。
Tips:Gradle 將遵循全部依賴關係的傳遞性。這意味着,若是一個依賴有它本身的依賴關係,這些依賴也會被拉取。
有關設置依賴關係的更多信息,請參閱 Gradle 用戶指南(這裏),和DSL文檔(這裏)。
Gradle 項目也能夠經過使用多項目設置依賴於其餘的 Gradle 項目。
一個多項目設置一般是經過讓全部的項目做爲給定根項目的子文件夾來實現。
例如,給定如下項目結構:
MyProject/ + app/ + libraries/ + lib1/ + lib2/
咱們能夠識別出3個項目。Gradle 將經過如下名稱引用它們:
:app
:libraries:lib1 :libraries:lib2
每個項目都有其本身的build.gradle文件,定義本身如何構建。
此外,在根路徑下還將有一個叫settings.gradle的文件用於聲明全部的項目。
這些文件的結構以下:
MyProject/
| settings.gradle + app/ | build.gradle + libraries/ + lib1/ | build.gradle + lib2/ | build.gradle
settings.gradle的內容很簡單:
include ':app', ':libraries:lib1', ':libraries:lib2'
它定義了哪一個文件夾其實是一個 Gradle 項目。
該:app項目可能依賴於libraries,這是經過聲明以下的依賴關係來配置的:
dependencies {
compile project(':libraries:lib1') }
在上面的多項目的設置中,:libraries:lib1和:libraries:lib2能夠是Java項目,而:app Android項目將會使用到它們的jar包輸出。
可是,若是你想共享訪問了 Android API或使用了 Android-style的資源的代碼,這些庫項目就不能是普通的Java項目,而應該是 Android Library 項目。
建立庫項目
Library項目與普通的 Android 項目很是類似,只有一些不一樣。
因爲構建庫項目與構建應用程序有些不一樣不一樣,因此使用的是不一樣的插件。這兩個插件內部共享了大部分的相同的代碼,而且它們都由一樣的com.android.tools.build.gradle jar 包提供。
buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.5.6' } } apply plugin: 'android-library' android { compileSdkVersion 15 }
上面的例子中建立了一個使用API 15編譯的庫項目。SourceSets和依賴關係與它們在應用程序項目中的處理方式同樣,而且支持一樣方式的自定義。
一個 Library 項目主要輸出的是一個.aar包(它表明Android的歸檔文件)。它組合了編譯代碼(如一個jar文件或原生的.so文件)和資源(manifest,res,assets)。
一個庫項目還能夠生成測試apk,獨立於應用程序測試這個庫。
庫項目用着一樣的錨任務(assembleDebug, assembleRelease),因此構建這樣一個項目的命令也沒有任何區別。
對於其餘的內容,庫項目和應用程序項目的行爲是同樣的。。他們都有構建類型(build types)和產品定製(product flavors),而且均可以生成多個版本的aar。
須要注意的是,Build Type的大部分配置都不適用庫項目。可是,您能夠根據一個庫項目是否被其餘項目使用仍是被測試,使用自定義 sourceSet 來更改庫項目的內容。
引用一個庫庫和引用其餘任何項目的方法是同樣的:
dependencies {
compile project(':libraries:lib1') compile project(':libraries:lib2') }
Tips: 若是您有多個庫,那麼排序將很是重要。這相似於舊的生成系統中, project.properties 文件的依賴項的順序的重要性。
默認狀況下,一個庫項目只發布它的release 變種程序。這變種程序將被全部引用該庫的項目使用,不管那些項目構建的是哪一種variant。這是因爲 Gradle 限制而有的一個臨時限制,咱們正在努力消除這個問題。
您能夠控制要發佈哪個以下所示:
android {
defaultPublishConfig "debug" }
Tips,這個發佈的配置名稱引用的是完整的 變種程序
名稱。release和debug,只在沒有定義flavor時適用。若是你想在使用flavors時更改默認的發佈
你能夠這樣寫:
android {
defaultPublishConfig "flavor1Debug" }
發佈一個庫項目的全部變種程序也是能夠作到的。咱們正計劃在正常的項目對項目(project-to-project)的依賴(如上面的例子)時也能夠這樣作,但如今由於 Gradle 的限制(咱們也在努力修復這些問題),還沒法作到。
默認狀況下沒有啓用發佈全部變種程序。要啓用它們
android {
publishNonDefault true }
變種程序意味着發佈多個aar文件,而不是發佈一個包含多個變種程序的aar文件,能意識到這一點是很是重要的。每個 aar 包都是包含一個單一的變種程序。
發佈一個變種程序,意識着讓這個可用的 aar 做爲 Gradle 項目的輸出文件。這個文件將會在發佈到一個maven倉庫中,或者另外一個項目建立對這個項目依賴時用到。
Gradle 有一個默認文件的概念。它就是在編寫下面的代碼時用到的:
compile project(':libraries:lib2')
若要建立對一個項目的另外一個已發佈的文件的依賴,您須要指定使用哪個:
dependencies {
flavor1Compile project(path: ':lib1', configuration: 'flavor1Release') flavor2Compile project(path: ':lib1', configuration: 'flavor2Release') }