隨着Google
對Eclipse
的無情拋棄以及Studio
的不斷壯大,Android
開發者逐漸拜倒在Studio
的石榴裙下。
而做爲Studio
的默認編譯方式,Gradle
已逐漸普及。我最開始是被它的多渠道打包所吸引。接下來咱們就係統的學習一下Gradle
。java
Gradle
是以Groovy
語言爲基礎,面向Java
應用爲主。基於DSL(Domain Specific Language)
語法的自動化構建工具。android
Gradle
集合了Ant
的靈活性和強大功能,同時也集合了Maven
的依賴管理和約定,從而創造了一個更有效的構建方式。憑藉Groovy
的DSL
和創新打包方式,Gradle
提供了一個可聲明的方式,並在合理默認值的基礎上描述全部類型的構建。 Gradle
目前已被選做許多開源項目的構建系統。git
由於Gradle
是基於DSL
語法的,若是想看到build.gradle
文件中所有能夠選項的配置,能夠看這裏
DSL Referencegithub
一個Gradle
項目經過一個在項目根目錄中的build.gradle
文件來描述它的構建。app
Build
文件最簡單的Android
應用中的build.gradle
都會包含如下幾個配置: Project
根目錄的build.gradle
:ide
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.5.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }
Module
中的build.gradle
:工具
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.3" ... }
buildscript { ... }
配置了編譯時的代碼驅動. 這種狀況下,它聲明所使用的是jCenter
倉庫。還有一個聲明所依賴的在Maven
文件的路徑。這裏聲明的包含了Android
插件所使用的1.5.0版本的Gradle
. 注意:這隻會影響build
中運行的代碼,不是項目中。項目中須要聲明它本身所須要倉庫和依賴關係。apply plugin : com.android.application
,聲明使用com.androdi.application
插件。這是構建Android
應用所須要的插件。android{...}
配置了全部Android
構建時的參數。默認狀況下,只有編譯的目標版本以及編譯工具的版本是須要的。重要: 這裏只能使用com.android.application
插件。若是使用java
插件將會報錯。學習
module/src/main
下的目錄結構,由於有時候不少人把so
放到libs
目錄就會報錯:gradle
若是項目的結構不標準的時候,可能就須要去配置它。Android
插件使用了類似的語法,可是由於它有本身的sourceSets
,因此要在android
代碼塊中進行配置。下面就是一個從Eclipse
的老項目結構中配置主要代碼而且將androidTest
的sourceSet
設置給tests
目錄的例子:ui
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') } }
就像有些人就是要把so
放到libs
目錄中(這類人有點犟),那就須要這樣進行修改。
注意:由於在舊的項目結構中全部的源文件(Java
,AIDL
和RenderScript
)都放到同一個目錄中,咱們須要將sourceSet
中的這些新部件都設置給src
目錄。
對構建文件聲明插件時一般或自動建立一些列的構建任務去執行。無論Java
插件仍是Android
插件都是這樣。Android
常規的任務以下:
assemble
生成項目output
目錄中的內容的任務。check
執行全部的檢查的任務。build
執行assemble
和check
的任務。clean
清理項目output
目錄的任務。在Android
項目中至少會有兩種output
輸出:一個debug apk
和一個release apk
。他們都有本身的主任務來分別執行構建:
assemble
assembleDebug
assembleRelease
提示:Gradle
支持經過命令行執行任務首字母縮寫的方式。例如:
在沒有其餘任務符合aR
的前提下,gradle aR
與gradle assembleRelease
是相同的。
最後,構建插件建立了爲全部build type(debug, release, test)
類型安裝和卸載的任務,只要他們能被安裝(須要簽名)。
installDebug
installRelease
uninstallAll
uninstallDebug
uninstallRelease
uninstallDebugAndroidTest
Build
定製Android
插件提供了一些列的DSL
來讓直接從構建系統中作大部分的定製。
Manifest
總體部分DSL
提供了不少重要的配置manifest
文件的參數,例如:
minSdkVersion
targetSdkVersion
versionCode
versionName
applicationId
testApplicationId
testInstrumentationRunnder
Android Plugin DSL Reference提供了一個完整的構建參數列表。
把這些manifest
屬性放到build
文件中的一個重要功能就是它能夠被動態的設置。例如,能夠經過讀取一個文件或者其餘邏輯來獲取版本名稱。
def computeVersionName() { ... } android { compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig { versionCode 12 versionName computeVersionName() minSdkVersion 16 targetSdkVersion 23 } }
注意:不要使用可能與現有給定衝突的方法名。例如defaultConfig{...}
中使用getVersionName()
方法將會自動使用defaultConfig.getVersionName()
來帶起自定義的方法。
Build Types
默認狀況下Android
插件會自動將應用程序設置成有一個debug
版本和一個release
版本。
這就是經過調用BuildType
對象完成。默認狀況下會建立兩個實例,一個debug
實例和一個release
實例。Android
插件一樣容許經過其餘的Build Types
來定製其餘的實例。這就是經過buildTypes
來設置的:
android { buildTypes { debug { applicationIdSuffix ".debug" } jnidebug { initWith(buildTypes.debug) applicationIdSuffix ".jnidebug" jniDebuggable true } } }
上面的代碼執行了如下操做:
配置了默認debug
的Build Type
:
applicationId
。這樣debug
模式就能與release
模式的apk
同時安裝在同一手機上。jnidebug
的Build Type
,而且把它設置爲debug
的拷貝。JNI
組件的debug
和增長一個新的包名後綴來繼續定製該Build Type
。無論使用initWith()
仍是使用其餘的代碼塊,建立一個新的Build Types
都是很是簡單的在buildTypes
代碼塊中建立一個新的元素就能夠了。
爲應用簽名須要使用以下幾個部分:
A keystore
A keystore password
A key alias name
A key password
The store type
默認狀況下有一個debug
的配置,設置了一個debug
的keystore
,有一個已知的密碼。debug keystore
的位置是在$HOME/.android/debug.keystore
,若是沒有的話他會被默認建立。Debug
的Build Type
會默認使用該debug
的簽名設置。
固然也能夠經過使用DSL
語法中的signingconfigs
部分來建立其餘的配置來進行定製:
android { signingConfigs { debug { storeFile file("debug.keystore") } myConfig { storeFile file("other.keystore") storePassword "android" keyAlias "androiddebugkey" keyPassword "android" } } buildTypes { foo { signingConfig signingConfigs.myConfig } } }
上面的設置將把debug keystore
的位置改成項目的根目錄。一樣也建立了一個新的簽名配置,而且有一個新的Build Type
使用它。
Dependencies, Android Libraries and Multi-project setup
Gradle
項目能夠依賴其餘的外部二進制包、或者其餘的Gradle
項目。
想要配置依賴一個外部jar
包,須要在compile
的配置中添加一個dependency
。下面的配置是添加了全部在libs
目錄的jar
包:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) } android { ... }
注意:DSL
元素中的dependencies
是Gradle API
中的標準元素。不屬於andorid
元素。 compile
配置是用來編譯主應用的。它配置的全部部分都會被打包到apk
中。固然也有一些其餘的配置:
compile
: main application
androidTestCompile
:test application
debugCompile
:debug Build Type
release Compile
:release Build Type
固然咱們可使用compile
和<buildtype>.compile
這兩種配置。建立一個新的Build Type
一般會自動基於它的名字建立一個新的配置部分。這樣在像debug
版本而release
版本不適用的一些特別的library
時很是有用。
Gradle
只是使用Maven
和Ivy
倉庫。可是倉庫必需要添加到列表中,而且必須聲明所依賴倉庫的Maven
或者Ivy
定義。
repositories { jcenter() } dependencies { compile 'com.google.guava:guava:18.0' } android { ... }
注意:jcenter()
是指定倉庫URL
的快捷設置。Gradle
支持遠程和本地倉庫。
注意:Gradle
會直接識別全部的依賴關係。這就意味着若是一個依賴庫自身又依賴別的庫時,他們會被一塊兒下下來。
AAR
庫dependencies { compile(name:'本地aar庫的名字,不用加後綴', ext:'aar') }
Gradle
項目一般使用多項目設置來依賴其餘的gradle
項目。例如:
MyProject/
libraries/
Gradle
會經過下面的名字來引用他們: :app
:libraries:lib1
:libraries:lib2
每一個項目都會有一個單獨的build
文件,而且在項目的根目錄還會有一個setting.gradle
文件:
MyProject/
app/
libraries/
lib1/
lib2/
setting.gradle
文件中的內容很是簡單。它指定了哪一個目錄是Gralde
項目:
include ':app', ':libraries:lib1', ':libraries:lib2'
:app
這個項目可能會依賴其餘的libraries
,這樣能夠經過以下進行聲明:
dependencies { compile project(':libraries:lib1') }
Library
項目上面用到了:libraries:lib1
和:libraries:lib2
能夠是Java
項目,:app
項目會使用他們倆的輸出的jar
包。可是若是你須要使用android
資源等,這些libraries
就不能是普通的Java
項目了,他們必須是Android Library
項目。
Library
項目Library
項目和普通的Android
項目的區別比較少,因爲libraries
的構建類型與應用程序的構建不一樣,全部它會使用一個別的構建插件。可是他們所使用的插件內部有不少相同的代碼,他們都是由com.android.tools.build.gradle
這個jar
包提供的。
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.3.1' } } apply plugin: 'com.android.library' android { compileSdkVersion 23 buildToolsVersion "23.0.1" }
Library
項目的區別Library
項目的主要輸出我.aar
包。它結合了代碼(例如jar
包或者本地.so
文件)和資源(manifest
,res
,assets
)。每一個library
也能夠單獨設置Build Type
等來指定生成不一樣版本的aar
。
Lint Support
你能夠經過指定對應的變量來設置lint
的運行。能夠經過添加lintOptions
來進行配置:
android { lintOptions { // turn off checking the given issue id's disable 'TypographyFractions','TypographyQuotes' // turn on the given issue id's enable 'RtlHardcoded','RtlCompat', 'RtlEnabled' // check *only* the given issue id's check 'NewApi', 'InlinedApi' } }
Build
變量構建系統的一個目標就是能對同一個應用建立多個不一樣的版本。
Product flavors
一個product flavor
能夠針對一個項目制定不一樣的構建版本。一個應用能夠有多個不一樣的falvors
來改變生成的應用。 Product flavors
是經過DSL
語法中的productFlavors
來聲明的:
android { .... productFlavors { flavor1 { ... } flavor2 { ... } } }
Build Type + Product Flavor = Build Variant
像咱們以前看到的,每一個Build Type
都會生成一個apk
.Product Flavors
也是一樣的:項目的輸出殭屍全部Build Types
與Product Flavors
的結合。每種結合方式稱之爲Build Variant
。例如,若是有debug
和release
版本的Build Types
,上面的例子就會生成4種Build Variants
:
Flavor1
- debug
Flavor1
- release
Flavor2
- debug
Flavor2
- release
沒有配置flavors
的項目仍然有Build Variants
,它只是用了一個默認的flavor/config
,沒有名字,這致使variants
的列表和Build Types
的列表比較相同。
Product Flavor
配置android { ... defaultConfig { minSdkVersion 8 versionCode 10 } productFlavors { flavor1 { applicationId "com.example.flavor1" versionCode 20 } flavor2 { applicationId "com.example.flavor2" minSdkVersion 14 } } }
注意android.productFlavors.*
對象ProductFlavor
有android.defaultConfig
是相同的類型。這就意味着他們有相同的屬性。 defaultConfig
爲全部的flavors
提供了一些基本的配置,每一個flavor
都已重寫他們。在上面的例子中,這些配置有:
flavor1
applicationId
: com.example.flavor1
minSdkVersion
: 8versionCode
: 20flavor2
applicationId
: com.example.flavor2
minSdkVersion
: 14versionCode
: 10一般,Build Type
配置會覆蓋其餘的配置。例如,Build Type
的applicationIdSuffix
會添加到Product Flavor
的applicationId
上。
最後,就像Build Types
同樣,Product Flavors
也能夠有他們本身的依賴關係。例如,若是有一個單獨的flavors
會使用一些廣告或者支付,那這個flavors
生成的apk
就會使用廣告的依賴,而其餘的flavors
就不須要使用。
dependencies { flavor1Compile "..." }
BuildConfig
在編譯階段,Android Studio
會生成一個叫作BuildConfig
的類,該類包含了編譯時使用的一些變量的值。你能夠觀看這些值來改變不一樣變量的行爲:
private void javaCode() { if (BuildConfig.FLAVOR.equals("paidapp")) { doIt(); else { showOnlyInPaidAppDialog(); } }
下面是BuildConfig
中包含的一些值:
boolean DEBUG
- if the build is debuggable
int VERSION_CODE
String VERSION_NAME
String APPLICATION_ID
String BUILD_TYPE
- Build Type
的名字,例如release
String FLAVOR
- flavor
的名字,例如flavor1
ProGuard
配置Android
插件默認會使用ProGuard
插件,而且若是Build Type
中使用ProGuard
的minifyEnabled
屬性開啓的話,會默認建立對應的task
。
android { buildTypes { release { minifyEnabled true proguardFile getDefaultProguardFile('proguard-android.txt') } } productFlavors { flavor1 { } flavor2 { proguardFile 'some-other-rules.txt' } } }
Tasks
控制基本的Java
項目有一系列的tasks
一塊兒製做輸出文件。 classes task
就是編譯Java
源碼的任務。 咱們能夠在build.gradle
中經過使用classes
很簡單的獲取到它。就是project.tasks.classes
.
在Android
項目中,更多的編譯task
,由於他們的名字經過Build Types
和Product Flavors
生成。
爲了解決這個問題,android
對象有兩種屬性:
applicationVariants
- only for the app plugin
libraryVariants
- only for the library plugin
testVariants
- for both plugins
這些都會返回一個ApplicationVariant
, LibraryVariant
,TestVariant
的DomainObjectCollection
接口的實現類對象。 DomainObjectCollection
提供了直接獲取或者很方便的間接獲取全部對象的方法。
android.applicationVariants.all { variant -> .... }
可使用compileOptions
代碼塊來設置編譯時使用的語言版本。默認是基於compileSdkVersion
的值。
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 targetCompatibility JavaVersion.VERSION_1_6 } }
Resource Shrinking
Gradle
構建系統支持資源清理:對構建的應用會自動移除無用的資源。不只會移除項目中未使用的資源,並且還會移除項目因此來的類庫中的資源。注意,資源清理只能在與代碼清理結合使用(例如ProGuad
)。這就是爲何它能移除所依賴類庫的無用資源。一般,類庫中的全部資源都是使用的,只有類庫中無用代碼被移除後這些資源纔會變成沒有代碼引用的無用資源。
android { ... buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }