本篇文章已授權微信公眾號 dasu_Android(大蘇)獨家發布
Android Studio 這么強大的工具,就算我們不懂 gradle, groovy, 也照樣能借助AS對 Android 項目進行編譯、調試、運行、打包等操作。build.gradle 這個文件接觸這么久了,基本的項目配置也基本很熟悉了,畢竟每次自動創建的 build.gradle 里的代碼就那么幾項配置,看一下那些英文單詞也基本猜到是什么配置。
但是,不知道你們會不會跟我一樣,在 github 上 clone 大神的項目后,總會發現他們的 build.gradle 里多了很多平常沒看見過的代碼,而且還看不懂代碼要做什么;
或者是比如當需要進行簽名時,網上資料會讓你在 Android 標簽內加個 signingConfigs, 然后在它里面進行各種配置,比如 storeFile, keyAlias 等等之類的。還有其他類似這種情況,比如當需要打包時,在哪個地方加個什么標簽再對它進行各種配置之類的。不知道你們會不會也跟我一樣會有這樣的疑問,這個標簽名怎么來的,為什么要放在這個位置,它里面有哪些屬性可以進行配置?
疑惑久了,總想去了解下這是為什么,所以花了一段時間來學習 gradle 的相關知識,這次在這里記錄也分享一下,如果有錯的地方,還望指點一下。
本次計划是寫個 gradle 系列博客,大概會有3-4篇,第一篇只是簡單的針對某個具體的 build.gradle 文件代碼進行注釋解釋以及拋出一些疑問,當然這個 build.gradle 不會是AS自動創建的那么簡單的代碼。然后再寫1-2篇介紹 gradle, groovy, 相關的資料網上很多,所以不會寫得很基礎,大概是挑選一些我認為比較重要的知識點進行介紹。最后在前面的基礎上,對 build.gradle 里面的代碼進行分析講解,比如介紹說都有哪些標簽,哪里去找這些標簽等等。
好了,廢話就嘮叨到這,下面就開始正文。
系列索引
build.gradle系列一:看不懂的build.gradle代碼
build.gradle系列二:學點Groovy來理解build.gradle代碼
build.gradle系列三:如何用Adnroid Studio查看build.gradle源碼
...
build.Gradle
這個 build.Gradle 文件來自 drakeet 大神的 Meizi 項目
我直接在代碼上加注釋,參照着注釋看代碼就行,是不是發現有很多代碼平時都沒看見過。
//Model都有各自的build.gradle,這里聲明該Model作為主項目,常見的還有另一個取值:
//apply plugin: 'com.android.library' 聲明該Model作為庫使用,當然還有其他取值,后面博客會介紹
apply plugin: 'com.android.application'
//這里是在as里引入一個retrolambda插件,具體我也不大懂,可以看看這篇博客:
//http://blog.csdn.net/zhupumao/article/details/51934317?locationNum=12
apply plugin: 'me.tatarka.retrolambda'
//這里是groovy的代碼了,定義了一個獲取時間的方法,groovy是兼容java,它可以直接使用jdk里的方法
def releaseTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}
//file()是Project.java里的一個方法,這里定義一個File類型的對象,Project后面博客會介紹到
def keyStore = file('meizhi.keystore')
android {
//這個大家應該很熟悉了,有疑問的應該是后面的代碼,這里表示獲取一些全局變量
//這些變量的值在根目錄下的build.gradle中定義,具體可以看看這篇博客:
//http://blog.csdn.net/fwt336/article/details/54613419
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
//同理,這里都是通過獲取全局設置的變量值來進行相關配置,這樣做的好處在於當
//你的項目里有多個model時,可以方便修改這些公共的配置,只需要修改一個地方就可以同步了
defaultConfig {
applicationId rootProject.ext.android.applicationId
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
}
//這里應該是設置打包后的apk里的META-INF移除指定的文件吧
packagingOptions {
exclude 'META-INF/DEPENDENCIES.txt'
//省略部分exclude 代碼...
}
//關閉指定的lint檢查
lintOptions {
disable 'MissingTranslation', 'ExtraTranslation'
}
//lint檢查到錯誤時不中斷編譯,好像是說lint檢查是為優化代碼,發現的錯誤其實並不會導致程序異常
//所以有的時候及時發現Lint檢查錯誤還是可以直接運行查看效果
lintOptions {
abortOnError false
}
//簽名的相關配置
signingConfigs {
//這個標簽名可以隨意命名,這里的作用大概類似於定義一個對象,該對象里設置好了簽名需要的各種配置
//可以定義不止一種配置的簽名對象,例如常見的還有 debug{...}, release{...},然后在buildTypes{}里
//通過 signingConfigs.app1 進行調用
app1 {
//簽名的相關配置,網上資料很多,STOREPASS, KEYALIAS, KEYPASS 這些常量是定義在
//gradle.properties 文件里,如果沒有該文件手動創建即可,這樣可以保證安全
//只有定義在 gradle.properties 里的常量才可以直接通過常量名引用
storeFile file('meizhi.keystore')
storePassword project.hasProperty('STOREPASS') ? STOREPASS : ''
keyAlias project.hasProperty('KEYALIAS') ? KEYALIAS : ''
keyPassword project.hasProperty('KEYPASS') ? KEYPASS : ''
}
}
//編譯,打包的項目配置
buildTypes {
debug {
//在 BuildConfig 里自定義一個 boolean 類型的常量
//更多資料可以查看:http://stormzhang.com/android/2015/01/25/gradle-build-field/
buildConfigField "boolean", "LOG_DEBUG", "true"
debuggable true
applicationIdSuffix ".debug"
}
release {
buildConfigField "boolean", "LOG_DEBUG", "false"
debuggable false
//開啟混淆
minifyEnabled true
//刪除無用的資源
shrinkResources true
//混淆文件
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
if (keyStore.exists()) {
println "Meizhi: using drakeet's key"
//根據在signingConfigs.app1里的簽名配置進行簽名
signingConfig signingConfigs.app1
} else {
println "Meizhi: using default key"
}
//這段代碼應該會在大神的項目里挺常見的,我在很多項目里都看見過了
//這也是groovy的代碼,這里的代碼作用是重命名最后打包出來的apk
//根據 def fileName 設置的格式來命名,${}表示的是某個變量的引用
//例如根據設置的格式最后apk命名可能是: Meizhi_v1.0.0_2017-03-28_fir.apk
//至於 applicationVariants 這些變量含義后面博客會介紹
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
def fileName = "Meizhi_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
//這里的作用跟 singingConfigs 差不多,只是為不同的 flavor 設置一些屬性
//常見的設置比如設置不同的渠道編號,設置不同的 api 服務器等等
productFlavors {
fir {
//這個的作用是將 AndroidManifest.xml 里的占位符 ¥{UMENG_CHANNEL_VALUE} 的值替換成fir
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "fir"]
}
GooglePlay {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "GooglePlay"]
}
Umeng {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "Umeng"]
}
}
}
//設置JDK的版本通過compileOptions
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
//lint的相關配置吧
lintOptions {
disable "InvalidPackage"
lintConfig file("lint.xml")
}
}
//這里就不用說了
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(":libraries:headsupcompat")
compile project(":libraries:smooth-app-bar-layout")
//as默認會去下載傳遞依賴,下面是指定不需要去下載傳遞依賴
compile ('com.squareup.retrofit2:retrofit:2.1.0') {
exclude module: 'okhttp'
}
retrolambdaConfig 'net.orfjackal.retrolambda:retrolambda:2.3.0'
//省略部分compile代碼...
}
疑問
1.apply plugin: 'com.android.application' 聽說這是調用一個方法?
2.rootProject.ext.android.compileSdkVersion, 不用 ext 來設置全局變量是否可以?
3.defaultConfig{}, packagingOptions{}, signingConfigs{}, buildTypes{} 等等這些,我怎么知道 Android{} 里都有哪些可以使用?
...
參考資料
·徐宜生寫的《Android群英傳:神兵利器》第4章:與Gradle的愛恨情仇
·retrolambda使用教程
·Gradle配置全局變量
·GRADLE自定義你的BUILDCONFIG
最近剛開通了公眾號,想激勵自己堅持寫作下去,初期主要分享原創的Android或Android-Tv方面的小知識,感興趣的可以點一波關注,謝謝支持~~