Gradle是Android Studio默認的構建工具,若是是基本的APP開發,不會涉及到Gradle太多內容,畢竟它的誕生就不是專爲Android服務的。java
平常開發須要涉及到使用Gradle的場景至關有限,比較頻繁的就是對應庫,如jar,.so文件的導入,若是應用自己方法數比較多,尤爲是導入太多第三方庫就容易出現這個問題,就須要用到MultiDex的相關內容,若是須要在編譯的時候區分debug和release等版本,是否混淆或者自動打包等,這些都會涉及到Gradle的編寫,但網上都有現成的例子,直接拿來用就能夠一直保持Gradle文件在應用版本迭代中基本保持不變。android
因此這裏有一個關鍵的地方:若是要學習Gradle,界限在哪裏。git
從Android開發人員角度來看,這是一個至關重要的問題,畢竟Gradle也是Android開發中至關重要的部分,若是對這塊不熟悉的話,就有種表面上走在康莊大道,但實際上倒是被固定住頭部不能往下看腳底踩的究竟是不是正常的路的感受。程序員
首先,咱們在建立Android應用程序的時候,Android Studio默認的Android結構已經顯示得很清楚的了:web
咱們來看看Gradle Scripts的相關文件。服務器
看一下第一個build.gradle文件。閉包
它後面的說明代表這是Project的build.gradle文件,也就是根目錄的build.gradle文件,而後咱們看一下文件的內容:app
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
buildScript在Groovy(Gradle的DSL,domain specific language,領域專門語言)中是一種method的調用,傳入的參數爲configuration closure,執行後會對Project的屬性進行配置。
dom
Closure,閉包,對於程序員來講,是一個至關熟悉的概念,不一樣語言都有閉包的實現和它們各自的意義,而Groovy是基於JVM的語言,它的閉包和Java是一致的,至關於一個匿名函數。eclipse
在Groovy中,咱們能夠這樣寫一個閉包:
{ a, b -> a + b }
若是參數只有一個,咱們能夠用it來替代:
{ it -> print it}
甚至能夠連這個it均可以省略,直接寫成:
{ print it }
所以,咱們能夠理解上面這個閉包爲何會這樣寫。
repositories表示代碼倉庫的下載來源,這裏的來源是jcenter。
Gradle支持的代碼倉庫有幾種類型:
(1)Maven中央倉庫,不支持https訪問,聲明方法爲mavenCentral()
(2)JCenter中央倉庫,實際上也是用Maven搭建,經過CDN分發,而且支持https訪問,也就是咱們上面默認的聲明方法:jcenter,若是咱們想要切換成http訪問,就要修改配置:
repositories { jcenter { url "http://jcenter.bintray.com" } }
(3)Maven本地倉庫,能夠經過本地配置文件配置,經過USER_HOME/.m2/
下的settings.xml配置文件修改默認路徑位置,聲明方法爲mavenLocal()
(4)常規的第三方maven庫,須要設置訪問url,聲明方法爲maven,這個通常是有本身的maven私服
(5)Ivy倉庫,能夠是本地倉庫,也能夠是遠程倉庫
(6)直接使用本地文件夾做爲倉庫,聲明以下:
repositories { flatDir { dirs 'lib' } flatDir { dirs 'lib1', 'lib2' } }
Gradle會優先使用服務器倉庫,若是沒有才會去找本地倉庫。
通常的項目需求採用的第三方基本都會提交到JCenter,因此大部分狀況下直接使用默認的就行,考慮到國內訪問速度,能夠提早下載好對應的庫,而後放到本身或者公司的maven私服上,再指定對應的maven地址。
dependencies代表項目依賴對應版本的Gradle構建工具,但更加具體的版本信息倒是在gradle-wrapper.properties這個文件中,具體如:
#Mon Dec 28 10:00:20 PST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
咱們下載下來的是gradle-2-10。
allprojects指定全部參與構建的項目使用的倉庫的來源。
這裏咱們有一個疑問:buildscript和allprojects都指定使用的倉庫的來源,它們的真正區別在哪裏呢?
實際上,allprojects是用於多項目構建,在Android中,使用多項目構建,其實就是多Module構建。
咱們看settings.gradle這個文件:
include ':app'
經過include將app這個module添加進來。
從根目錄開始,一直到include進來的全部module,都會執行allprojects的內容。
咱們也能夠經過subprojects來指定module和根目錄不一樣的行爲。
咱們是否能夠指定某個module執行它特有的行爲呢?
固然能夠,經過project(':moduleName'){}就能指定該module本身的行爲。
在Android中,多項目的構建是很正常的,由於咱們添加的library也是一個module,不過通常而言,子項目和根項目的配置大部分狀況下都是同樣的,大部分操做都是在app module中,而其餘library module只是提供API而已。
buildscript主要是爲了Gradle腳本自身的執行,獲取腳本依賴插件,在Android中,咱們的構建腳本就是Gradle。
repositories也能夠是根級別的,爲當前項目提供所需的依賴包,但在Android中,這個跟allprojects的repositories的做用是同樣的。
一樣dependencies也能夠是根級別的。
咱們在Android Studio 2.0正式版本中生成應用的根目錄的build.gradle文件中,結尾還看到這樣的代碼:
task clean(type: Delete) {
delete rootProject.buildDir
}
這裏聲明瞭一個clean的task,它會在咱們執行gradle clean時,刪除根目錄的build目錄。
看一下這個面板:
能夠看到各類task,咱們的應用之因此可以編譯,運行和安裝,都是這些task在發揮做用。
咱們再看一下proguard-rules.pro這個文件,這個是混淆文件,在這裏添加項目的混淆規則。
gradle.properties能夠設置gradle喚起的daemon進程,好比JVM參數,若是Gradle編譯速度和網速沒有關係,那麼有多是Gradle的daemon進程的JVM參數過小了,所以能夠在這裏進行配置。
local.properties是指定SDK的存放地址,在Android開發中,.gitignore文件默認就有忽略這個文件的設置,由於每一個人的SDK位置都有多是不一樣的。
Android Studio提供了多種工程目錄結構,其中最多見的是Android和Project,Project和Eclipse是同樣的,而.gitignore也只有在Project工程結構下才能看到。
根目錄的builde.gradle文件咱們通常都不會太多去改,絕大部分的工做都是在對應module的builde.gradle文件。
咱們看一下app的build.gradle文件:
apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion "25.0.3" defaultConfig { applicationId "com.example.weber_zheng.myapplication" minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:25.3.1' }
apply plugin代表應用的插件的類型,這裏是com.android.application類型,而庫工程則是com.android.library,若是是java項目,則是java。
Gradle中的plugin能夠理解爲一個已經定義好的模塊,咱們只要經過apply添加進來便可,對於Android開發來講,只要知道本身的module須要引入的插件是application仍是library就能夠了,固然,若是確實須要第三方插件,也能夠導進來,相似bugtags這種插件。
android中就是Android插件的相關配置,咱們不少操做基本都是在這裏。
咱們關注buildTypes,這裏只有release一種編譯類型,實際上,能夠添加debug等其餘自定義的類型。
在buildTypes中,minifyEnabled控制是否開啓混淆,而proguardFiles指定了混淆文件,就是前面提到的proguard-rules.pro文件。
dependencies和build.gradle文件中的dependencies是同樣的做用,若是咱們想要編譯工程,能夠compile project(':projectName'),編譯aar包,能夠compile(name: 'aarName', ext: 'aar')。
通常build.gradle在配置完成後,都不會有太大的改動,而常常改動的地方就是添加依賴的時候。
咱們能夠在dependencies中添加依賴,依賴的類型常見是三種:jar,.so,aar和project。
默認的builde.gralde文件都會有這一句:compile fileTree(dir: 'libs', include: ['*.jar']),它表示編譯libs目錄下的.jar文件,因此若是咱們有新的jar包,都放在libs目錄裏面,若是make工程都沒反應,能夠在Gradle那裏點擊同步。
但若是有需求,須要放在libs的子目錄呢?
這時候是找不到這個.jar文件的,能夠在compile中顯示的指定.jar文件的具體路徑,不過也能夠更加簡單點,fileTree的參數是一個Map,dir只能指定一個目錄,可是include能夠指定多個,所以能夠修改成相似這樣['*.jar'], ['test/*.jar']。
若是咱們想要編譯.so文件,就要指定jinLibs的目錄,而這個動做是經過sourceSets類執行:
sourceSets {
main {
jniLibs.srcDirs = ['libs']//指定lib庫目錄
}
}
這裏指定了jinLibs的目錄是libs,所以咱們能夠將.so文件直接放在libs目錄下。
Android開發中,不可避免的會導入各類第三方庫,但若是第三方庫自己也導入了和咱們主工程同樣的庫,就會報錯,所以要排除第三方庫中重複的庫:
compile('org.eclipse.paho:org.eclipse.paho.android.service:1.0.2') { exclude(group: 'com.google.android', module: 'support-v4') }
以上是建立一個APP時候,Android Studio默認的Gradle相關的內容,接下來咱們來看一下在具體的生產環境中如何對應具體的需求修改build.gradle文件。
在應用開發中,正式環境和測試環境的ip地址確定是不同的,所以測試版本和正式版本在打包的時候須要切換對應的環境。
咱們之前採用很笨的作法:設置一個靜態變量,表示服務器的ip地址,而後有一個被註釋掉的同名變量,表示測試地址,打包時候分別註釋對應的地址。
很顯然,這樣的作法至關愚蠢。
因此,後來咱們採起了另外一種作法:設置一個debug開關,控制服務器地址的切換。
如今好多了,但仍是須要去修改代碼設置debug的值。
固然,咱們能夠在測試的時候,指定build的類型是debug,Android Studio的debug包和release包是有區分的,但不少第三方都要有簽名才能測試,而debug包的默認簽名是沒法測試的,因此問題的癥結就在於:如何打包都有正式簽名的測試包和發佈包,並且不須要修改代碼。
Gradle能夠經過buildTypes作到這一點:
buildTypes { debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' buildConfigField "int", "CONFIG_MODE", "1" buildConfigField "int", "LOG_SWITCH", "1" } release { signingConfig signingConfigs.myConfig minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' buildConfigField "int", "CONFIG_MODE", "2" buildConfigField "int", "LOG_SWITCH", "0" } }
咱們能夠添加buildConfigField,這裏添加了一個CONFIG_MODE表示配置的模式,0表示relese,1表示debug,而後經過BuildConfig.CONFIG_MODE獲取這個值,在代碼中進行判斷,這樣只要在這裏進行設置就能夠:
BuildConfig是自動生成的文件,它裏面存放build.gradle配置的值:
public final class BuildConfig { public static final boolean DEBUG = Boolean.parseBoolean("true"); public static final String APPLICATION_ID = "com.wenjiang.http"; public static final String BUILD_TYPE = "debug"; public static final String FLAVOR = ""; public static final int VERSION_CODE = 1; public static final String VERSION_NAME = "1.0"; // Fields from build type: debug public static final int CONFIG_MODE = 1; public static final int LOG_SWITCH = 1; }
看到這裏,咱們想到了能夠更好的精簡代碼。
咱們能夠從新添加一個CONFIG_IP,這樣代碼中就不須要設置ip常量,也不用寫判斷語句,不過要注意String類型要添加一對雙引號,這樣生成的時候纔會是正確的字符類型。
buildTypes還有其餘的屬性能夠配置,例如shrinkResources能夠用於指定刪除無效的Resource。
可是這個屬性有個問題:咱們應用中有些資源是主題指定資源,是在確認加載主題的時候纔會去添加,相似這樣的使用方式:
getResources().getIdentifier(key, 「drawable」,getPackageName()));
因此這些資源在編譯打包的時候,由於沒有引用,會被認爲是無用資源而被移除,這樣確定是有問題的。
可是目前咱們沒法還原真實的場景,因此暫時跳過這部分。
buildTypes還能夠指定生成的apk的名字後綴,這樣咱們就能在同一部測試機上同時安裝debug和release版本,這個能夠經過applicationIdSuffix ".debug"進行指定。
applicationId用於標識咱們的應用,默認狀況下是應用的包名,不分debug和release,因此在兩個環境切換的時候,都須要從新安裝,若是不想從新安裝,就要爲其中一個版本指定後綴。
一樣還有一個versionNameSuffix能夠在版本名字後面添加後綴。
應用在正式發佈後,能夠經過debuggable,jniDebuggable和renderscriptDebuggable設置是否可調試。
這裏有一個zipAlignEnabled屬性要值得注意。
這個屬性設置是否對APK包執行ZIP對齊優化,而這個跟應用程序運行時優化有關。
ZipAlign在Android 1.6的時候引入,可以對打包的應用程序進行優化,使Android操做系統與應用程序間的交互做用更有效率,所以運行得更快。
它究竟是怎麼作到的呢?
ZipAlign對apk文件中未壓縮的數據在4個字節邊界上對齊,這樣Android系統就能夠經過調用mmap函數讀取文件,這樣進程就能夠經過映射同一個普通文件實現內存共享,所以能夠像訪問普通內存同樣對文件進行訪問,沒必要調用read()和write()等操做。
而4個字節邊界上對齊,編譯器就能按照4個字節爲單位進行讀取,CPU就能對變量進行高效快速的訪問。
Android系統中的Davlik虛擬機使用的是本身專有的DEX格式,DEX的結構是緊湊的,而對齊能夠進一步優化。
所以,ZipAlign可以加快系統從APK文件中讀取資源,而且減小這個過程當中耗費的內存。
buildTypes能夠設置renderScript的相關屬性,而renderScript通常的應用都不會涉及到,常見的例子就是使用到高斯模糊的時候,爲了優化高斯模糊效果和性能,因此這部分就跳過。
buildTypes還能夠經過proguardFiles來指定多個混淆文件,但這個需求也是比較少用到。
signingConfig用於指定簽名的配置文件,咱們能夠在build.gradle文件中設置對應的配置:
signingConfigs {
myConfig {
storeFile file("android.keystore") //簽名文件
storePassword "****"
keyAlias "android"
keyPassword "****" //簽名密碼
}
而後經過signingConfig來指定。
若是新的buildTypes大部分和另外一個buildTypes同樣,咱們能夠經過jnidebug.initWith(buildTypes.debug)來複制debug的內容,而後在jnidebug中設置本身的屬性。
有關buildTypes的大概內容就是這樣,經常使用的屬性和相關的配置,意義也大概介紹了,通常的應用也大概只須要了解到這種程度,至少咱們的應用就是這樣。