android build.gradle(groovy)

1、build.Gradle

這個 build.Gradle 文件來自 drakeet 大神的 Meizi 項目
我直接在代碼上加註釋,參照着註釋看代碼就行,是否是發現有不少代碼平時都沒看見過。html

  1 //Model都有各自的build.gradle,這裏聲明該Model做爲主項目,常見的還有另外一個取值:
  2 //apply plugin: 'com.android.library' 聲明該Model做爲庫使用,固然還有其餘取值,後面博客會介紹
  3 apply plugin: 'com.android.application'
  4 
  5 //這裏是在as裏引入一個retrolambda插件,具體我也不大懂,能夠看看這篇博客: 
  6 //http://blog.csdn.net/zhupumao/article/details/51934317?locationNum=12
  7 apply plugin: 'me.tatarka.retrolambda'
  8 
  9 //這裏是groovy的代碼了,定義了一個獲取時間的方法,groovy是兼容java,它能夠直接使用jdk裏的方法
 10 def releaseTime() {
 11     return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
 12 }
 13 
 14 //file()是Project.java裏的一個方法,這裏定義一個File類型的對象,Project後面博客會介紹到
 15 def keyStore = file('meizhi.keystore')
 16 
 17 android {
 18 
 19     //這個你們應該很熟悉了,有疑問的應該是後面的代碼,這裏表示獲取一些全局變量
 20     //這些變量的值在根目錄下的build.gradle中定義,具體能夠看看這篇博客:
 21     //http://blog.csdn.net/fwt336/article/details/54613419
 22     compileSdkVersion rootProject.ext.android.compileSdkVersion
 23     buildToolsVersion rootProject.ext.android.buildToolsVersion
 24 
 25     //同理,這裏都是經過獲取全局設置的變量值來進行相關配置,這樣作的好處在於當
 26     //你的項目裏有多個model時,能夠方便修改這些公共的配置,只須要修改一個地方就能夠同步了
 27     defaultConfig {
 28         applicationId rootProject.ext.android.applicationId
 29         minSdkVersion rootProject.ext.android.minSdkVersion
 30         targetSdkVersion rootProject.ext.android.targetSdkVersion
 31         versionCode rootProject.ext.android.versionCode
 32         versionName rootProject.ext.android.versionName
 33     }
 34 
 35     //這裏應該是設置打包後的apk裏的META-INF移除指定的文件吧
 36     packagingOptions {
 37         exclude 'META-INF/DEPENDENCIES.txt'
 38         //省略部分exclude 代碼...
 39     }
 40 
 41     //關閉指定的lint檢查
 42     lintOptions {
 43         disable 'MissingTranslation', 'ExtraTranslation'
 44     }
 45 
 46     //lint檢查到錯誤時不中斷編譯,好像是說lint檢查是爲優化代碼,發現的錯誤其實並不會致使程序異常
 47     //因此有的時候及時發現Lint檢查錯誤仍是能夠直接運行查看效果
 48     lintOptions {
 49         abortOnError false
 50     }
 51 
 52     //簽名的相關配置
 53     signingConfigs {
 54         //這個標籤名能夠隨意命名,這裏的做用大概相似於定義一個對象,該對象裏設置好了簽名須要的各類配置
 55         //能夠定義不止一種配置的簽名對象,例如常見的還有 debug{...}, release{...},而後在buildTypes{}裏
 56         //經過 signingConfigs.app1 進行調用
 57         app1 {
 58             //簽名的相關配置,網上資料不少,STOREPASS, KEYALIAS, KEYPASS 這些常量是定義在
 59             //gradle.properties 文件裏,若是沒有該文件手動建立便可,這樣能夠保證安全
 60             //只有定義在 gradle.properties 裏的常量才能夠直接經過常量名引用
 61             storeFile file('meizhi.keystore')
 62             storePassword project.hasProperty('STOREPASS') ? STOREPASS : ''
 63             keyAlias project.hasProperty('KEYALIAS') ? KEYALIAS : ''
 64             keyPassword project.hasProperty('KEYPASS') ? KEYPASS : ''
 65         }
 66     }
 67 
 68     //編譯,打包的項目配置
 69     buildTypes {
 70 
 71         debug {
 72             //在 BuildConfig 裏自定義一個 boolean 類型的常量
 73             //更多資料能夠查看:http://stormzhang.com/android/2015/01/25/gradle-build-field/ 
 74             buildConfigField "boolean", "LOG_DEBUG", "true"
 75             
 76             debuggable true
 77             applicationIdSuffix ".debug"
 78         }
 79 
 80         release {
 81             buildConfigField "boolean", "LOG_DEBUG", "false"
 82 
 83             debuggable false
 84             
 85             //開啓混淆
 86             minifyEnabled true
 87             //刪除無用的資源
 88             shrinkResources true
 89             //混淆文件
 90             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 91             if (keyStore.exists()) {
 92                 println "Meizhi: using drakeet's key"
 93                 //根據在signingConfigs.app1裏的簽名配置進行簽名
 94                 signingConfig signingConfigs.app1
 95             } else {
 96                 println "Meizhi: using default key"
 97             }
 98 
 99             //這段代碼應該會在大神的項目裏挺常見的,我在不少項目裏都看見過了
100             //這也是groovy的代碼,這裏的代碼做用是重命名最後打包出來的apk
101             //根據 def fileName 設置的格式來命名,${}表示的是某個變量的引用
102             //例如根據設置的格式最後apk命名多是: Meizhi_v1.0.0_2017-03-28_fir.apk
103             //至於 applicationVariants 這些變量含義後面博客會介紹
104             applicationVariants.all { variant ->
105                 variant.outputs.each { output ->
106                     def outputFile = output.outputFile
107                     if (outputFile != null && outputFile.name.endsWith('.apk')) {
108                         def fileName = "Meizhi_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
109                         output.outputFile = new File(outputFile.parent, fileName)
110                     }
111                 }
112             }
113         }
114 
115         //這裏的做用跟 singingConfigs 差很少,只是爲不一樣的 flavor 設置一些屬性
116         //常見的設置好比設置不一樣的渠道編號,設置不一樣的 api 服務器等等
117         productFlavors {
118             fir {
119                 //這個的做用是將 AndroidManifest.xml 裏的佔位符 ¥{UMENG_CHANNEL_VALUE} 的值替換成fir
120                 manifestPlaceholders = [UMENG_CHANNEL_VALUE: "fir"]
121             }
122             GooglePlay {
123                 manifestPlaceholders = [UMENG_CHANNEL_VALUE: "GooglePlay"]
124             }
125             Umeng {
126                 manifestPlaceholders = [UMENG_CHANNEL_VALUE: "Umeng"]
127             }
128         }
129     }
130 
131     //設置JDK的版本經過compileOptions
132     compileOptions {
133         sourceCompatibility JavaVersion.VERSION_1_8
134         targetCompatibility JavaVersion.VERSION_1_8
135     }
136 
137     //lint的相關配置吧
138     lintOptions {
139         disable "InvalidPackage"
140         lintConfig file("lint.xml")
141     }
142 }
143 
144 //這裏就不用說了
145 dependencies {
146     compile fileTree(dir: 'libs', include: ['*.jar'])
147     compile project(":libraries:headsupcompat")
148     compile project(":libraries:smooth-app-bar-layout")
149     //as默認會去下載傳遞依賴,下面是指定不須要去下載傳遞依賴
150     compile ('com.squareup.retrofit2:retrofit:2.1.0') {
151         exclude module: 'okhttp'
152     }
153     retrolambdaConfig 'net.orfjackal.retrolambda:retrolambda:2.3.0'
154     //省略部分compile代碼...
155 }

 

疑問

1.apply plugin: 'com.android.application' 據說這是調用一個方法?java

2.rootProject.ext.android.compileSdkVersion, 不用 ext 來設置全局變量是否能夠?android

3.defaultConfig{}, packagingOptions{}, signingConfigs{}, buildTypes{} 等等這些,我怎麼知道 Android{} 裏都有哪些可使用?git

...github

·徐宜生寫的《Android羣英傳:神兵利器》第4章:與Gradle的愛恨情仇
·retrolambda使用教程
·Gradle配置全局變量
·GRADLE自定義你的BUILDCONFIGswift

2、Groovy 是什麼

Groovy 是一種腳本語言,既然是腳本語言,那麼它也就有腳本語言的那些特色:使用動態類型、末尾不用分號等等。另外,它又是基於 Java 上設計的語言,也就是 Groovy 兼容 Java,可使用 JDK 裏的各類方法,你能夠在 Groovy 文件裏寫 Java 代碼裏,照樣能夠正常編譯運行。api

Groovy 語法

關於語法的詳細的介紹在末尾有連接,這裏就只是挑出我認爲比較重要的,並且跟 java 有區別的,在閱讀代碼時可能會看不懂的一些語法進行記錄。數組

1.註釋、標識符方面跟 Java 基本同樣。

2.基本數據類型,運算方面

這方面在 build.gradle 文件裏也不怎麼常見到使用,由於 groovy 是動態類型,定義任何類型均可以只使用 def 來定義,因此若是使用具體的好比 char, int 等類型時須要強制轉換吧。有須要的能夠本身查閱末尾的參考連接。安全

3.字符串方面

java 只支持用 "..." 雙引號來表示字符串服務器

groovy 支持使用 '...'"..."'''...'''"""..."""/.../$/.../$ 即單引號,雙引號等6種方法來表示字符串
至於各類表示方法有什麼區別,具體能夠參考末尾的連接,這裏簡單提提,'...'"..." 只支持單行字符串,不支持多行,剩下的四種都支持多行字符串,以下圖
Groovy字符串代碼示例
控制檯輸出結果

斜槓我也不多見,常見的是帶有 ${} 的字符串,好比: println "blog's url: ${blogUrl}" 這是 groovy 的 GString 特性,支持字符串插值,有點了相似於變量引用的概念,但注意,在 '...''''...''' 單引號表示的字符串裏不支持 ${}。固然,若是你要使用 java 的方式,用 + 來拼接也能夠。

4.集合方面(List、Map)

定義和初始化
定義很簡單,List 的話使用 [] 定義,各項用 , 隔開便可。Map 的話使用 [:],各項也是用 , 隔開,如:

 

有一點跟 java 不一樣的是, groovy 集合裏不要求每一項都是同類型,好比能夠這樣定義 def list = [1, 'dasu', true],集合裏包含數字,字符串,布爾值三種類型。

使用
經過下標操做符 [] 讀寫元素值,並使用正索引值訪問列表元素或負索引值從列表尾部訪問元素,也可使用範圍,或使用左移 << 追加列表元素,如

 

跟 java 不一樣的是, groovy 並不存在下標訪問越界,當下標爲負數時則從右開始算起,當指定的下標沒有存放值時返回 null。

5.數組方面

groovy 其實沒有嚴格區分數組和集合,數組的定義和使用方法跟集合同樣,只是你須要強制聲明爲數組,不然默認爲集合,如

 

上面的初始化方式是否是跟 java 不同,這一點須要注意下,java 是用 {} 來初始化,但在 groovy 裏面, {} 表示的是閉包,因此這點須要注意一下。


上面的是 groovy 與 java 不一樣的一些基本語法,下面介紹一些我本身認爲是 groovy 比較重要的特性,若是要看懂 build.gradle 裏的代碼,明白下面介紹的會比較有幫助。

6.方法的簡化使用

方法的括號能夠省略

groovy 定義方法時能夠不聲明返回類型和參數類型,也能夠不須要 return 語句,最後一行代碼默認就是返回值。
而在調用方法時能夠將括號省略,不省略的時候以下

 

上面的方式不陌生吧,再來看看下面的代碼

 

上面就是調用方法時省略掉圓括號的寫法,再來看一種狀況

 

此次定義一個參數爲 map 類型的方法,若是咱們在調用方法的時候纔對參數進行定義和初始化會是什麼樣的呢?以下

 

以前說過了,groovy 調用方法時能夠將括號省略掉,這樣一來再看下

 

這樣子的格式是否是看着以爲很眼熟,沒錯,就是 build.gradle 裏的第一行代碼。
build.gradle
若是有看過個人上一篇 build.gradle 博客的話,如今對疑問1是否是就有些理解了呢。

上圖那代碼若是把省略的括號補上的話,你們應該就會熟悉點了

 

調用了 apply() 方法,該方法傳入一個 map 參數,咱們來看看是否是這樣,用as查看下源碼,以下
PluginAware.java
沒錯吧,apply() 實際上是個方法,參數爲 map 類型,並且 key 的取值也給你規定了 frompluginto 三種,是否是確實在別人的 build.gradle 代碼裏也有看見過相似 apply from ***,這樣一來就明白多了吧。

好了,而後你再從新去看一下 build.gradle 裏的代碼,是否是對每一行的代碼都有了新的見解了。

其實 build.gradle 裏的每一行代碼都是在調用一個方法,好比下面這些咱們常見的:
build.gradle
每一行都是在調用一個方法,前面是方法名,後面是方法的參數,只是把括號省略掉了而已,感興趣的你能夠再本身用as點進去看看源碼是否是這樣。

方法最後一個參數是閉包能夠提取出來接到後面

閉包是 groovy 的一大特性,我理解也不深,也講不大清楚,感興趣的可自行網上查閱學習,簡單的說就是一個用 {..} 包起來的代碼塊,好比 build.gradle 裏的 defaultConfig{...}buildTypes{...}dependencies{...} 等等這些大括號包起來的代碼塊就是閉包,閉包代碼塊最後一句代碼做爲閉包的返回值。

當閉包做爲方法的最後一個參數,能夠將閉包從參數圓括號中提取出來接在最後,若是閉包是惟一的一個參數,則方法參數所在的圓括號也能夠省略。對於有多個閉包參數的,只要是在參數聲明最後的,都可以按上述方式省略,舉個例子。

 

上面定義一個 add 方法,最後一個參數爲閉包,調用的時候傳入一個閉包,閉包的最後一行代碼 1+1 做爲閉包返回值返回,閉包返回值做爲方法的第二個參數傳入方法中計算加法,因此最終輸出3。上面的調用也能夠寫成下面的方式:

 

注意,這是調用 add() 方法,而不是在定義,1 是第一個參數,括號後的閉包 { 1+2 } 是方法的第二個參數,這就是 groovy 的特性,閉包能夠提取出來。那麼再想一想,若是方法只有一個閉包參數,再結合 groovy 能夠省略掉括號的特性,這樣子調用一個方法將會是什麼樣子呢?

 

是否是又感受很熟悉,對吧,就是 build.gradle 裏的 defaultConfig{...}buildTypes{...}dependencies{...} 等等這些。

因此,結合上面講的兩點:能夠省略方法括號和閉包能夠提取接到括號後面,這樣一來, build.gradle 裏的代碼其實就是在調用各類方法,defaultConfig 是一個方法,compileSdkVersion 也是一個方法。 build.gradle 裏的每一行代碼前面是方法名,後面則是方法須要的參數,參數有的是基本類型,有的則是閉包類型。

集合遍歷 each/all
就先把上一篇博客裏的在一段在 build.gradle 裏很常見的代碼貼出來

重名名apk代碼

這段代碼做用就是對打包生成的 apk 按照規定的格式進行重命名,在不少大神的 build.gradle 裏都會碰見過,其實這一段代碼就是 groovy 代碼,all 和 each 是集合的一種操做,all 後面跟着的是一個參數爲 variant 的閉包,表示對 applicationVariants 集合裏全部的對象都運行後面的閉包,同理 each 後面也是跟着一個參數爲 output 的閉包,相似於 java 裏的 for 循環操做。因此這裏要理解的應該是 applicationVariants 表明的是什麼,這點我也還不是很懂,後面若是搞懂了的話會在以後的博客裏介紹出來。

另外,我還有個疑問來着, all 操做和 each 操做有什麼區別麼,感受都是對集合裏全部的元素進行操做,若是有懂的可以告知就太感謝了,查了挺多資料貌似還不是很明白。

參考資料

官方文檔
Groovy語言規範-語法(官方文檔翻譯)
Groovy操縱集合祕籍

 

3、查看groovy源碼

咱們來舉個例子,就像系列一的博客裏介紹的 build.gradle 裏有這樣一段代碼:
build.gradle
咦!這代碼是第一次在 build.gradle 裏看見過,是什麼意思呢?不怕,我有絕招:
Ctrl + 左鍵
Android Studio大法---看源碼,還有什麼是不能經過看源碼註釋解決的麼
BaseExtension.class
是跳到源碼了,但是爲何沒有方法的註釋說明呢,這方法是什麼鬼誰知道啊。再仔細看看 as 的提示,原來打開的是個 class 文件啊。
as提示
as 的提示那裏應該會有個下載和選擇源碼位置的按鈕的啊,搞不懂爲何不出現。

另外,咱們知道,as 通常會默認先打開 xxx-sources.jar 也就是 xxx 的 java 文件源碼,若是沒有源碼文件,纔會打開 xxx.jar 的 class 代碼。咱們看一下,打開的是什麼文件。
gradle-2.3.0.jar
沒錯,as 打開的是 gradle-2.3.0.jar,說明 as 沒有找到 gradle-2.3.0-sources.jar 源碼文件,咱們看一下究竟是不是這樣
as標題欄
as 標題欄會顯示你當前打開的文件的具體位置,好了,知道了 gradle-2.3.0.jar 在電腦裏的位置了,咱們到那個目錄下看看
gradle-2.3.0.jar本地路徑
只有一個 gradle-2.3.0.jar 文件和一個 pom 文件,正常的話應該還要有個 xxx-sources.jar 文件纔對,就像下面這樣
三個文件
由於這裏沒有 gradle-2.3.0-sources.jar 文件,因此 as 沒辦法打開帶有方法註釋的源碼文件了。既然知道問題,那麼就好解決了,as 的提示條也沒有下載的按鈕,那咱們就本身去下載好了
seach.maven.org
打開 maven 網站,在這裏能夠下載 gradle 插件。在搜索框中輸入 com.android.tools.build,爲何輸入這個,你打開 project 下的 build.gradle 文件看看就知道了
com.android.tools.build
search result
這些就是 gradle 插件,咱們在根目錄下的 build.gradle 配置的 gradle 版本其實就是來這裏下載的,應該是吧。
下載的文件
下載後獲得的就是這些文件了,接下去就是找到相應文件的位置,複製一份過去
gradle的本地路徑
打開相應的插件文件夾,選擇相應的版本,最後將下載的 xxx-sources.jar 複製一份進去,最後的樣子以下
三個文件
好了,大功告成,咱們再打開 as,記得 ReBuild 一下,而後再試試查看 build.gradle 源碼會是什麼樣子
BaseExtension.java
LintOptions.java
翻譯一下英文大概就是說, lintOptions.abortOnError = false 是設置即便 lint 檢查時發現錯誤也不中止構建程序的運行。

這種方法比去官方的 api 文檔裏查閱方便多了吧。再來看看幾個效果。
BaseExtension.java
這裏就能夠看到源碼裏介紹 Plugin 的值都有哪些,分別對應哪一個類,該去哪一個類看它的做用是什麼,幹什麼的。
AndroidConfig.java
藉助 as,咱們甚至能夠很容易的查到 android{...} 這個括號裏能使用的方法都有哪些,若是要看各自的做用是幹什麼的,再繼續點進去查看註釋就好了。是否是發現,咱們在 build.gradle 裏的 android{...} 使用過的標籤名原來都在這裏的啊。

相關文章
相關標籤/搜索