Android項目中如何用好構建神器Gradle?
摘要:本文作者賈吉鑫為大眾點評Android工程師,在進行團隊并行開發時,分庫遇到的問題很多都要通過Gradle腳本解決。Gradle雖為構建神器,但學習曲線比較陡峭,要想在Android項目中用好Gradle必須要做到三點。
最近在忙團隊并行開發的事情,主要是將各個團隊的代碼分庫,一方面可以降低耦合,為后面模塊插件化做鋪墊,另一方面采用二進制編譯,可以加快編譯速度。分庫遇到了一些問題,很多都要通過Gradle腳本解決,所以稍微花時間研究了一下。
Gradle雖為構建神器,但感覺學習曲線比較陡峭。Gradle User Guide內容很多,但有點太多了,多的你看不完,Gradle Plugin User Guide一篇文章主要講了Android相關的配置,看完可能感覺馬馬虎虎會用,但到了修改一些構建流程的時候還是不知所措。經過一段時間的摸索,我覺得在Android項目中用好Gradle,你要做到以下三點:
- 了解Groovy基本語法。
- 粗讀Gradle User Guide和Gradle Plugin User Guide。
- 實戰,實戰,再實戰。(三遍,你懂的) </ol>
-
比如為何在gradle腳本中使用InputStream不用import包,而使用ZipFile需要import包?因為groovy默認import了下面的包和類,無需再import.
java.io.* java.lang.* java.math.BigDecimal java.math.BigInteger java.net.* java.util.* groovy.lang.* groovy.util.*
</li> -
經常看到${var1}的用法是怎么回事? 這是Groovy中的GString,可以在雙引號中直接使用,用于字符串疊加非常方便。
def dx = tasks.findByName("dex${variant.name.capitalize()}")
</li> -
下面的代碼你真的能看懂嗎?
//apply是一個方法,plugin是參數,值為'com.android.application' apply plugin: 'com.android.application'
/*buildscript,repositories和dependencies本身是方法名。 后面跟的大括號部分,都是一個閉包,作為方法的參數。閉包可以簡單的理解為一個代碼塊或方法指針。 */ buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.2.3' } }
//groovy遍歷的一種寫法 each后面是閉包 android.applicationVariants.each { variant -> }</pre></li> </ol>
Gradle概念
下面講幾個Gradle相關的概念,幾個比較重要的吧,更多的東西還是要自己去看Gradle User Guide。
生命周期
Gradle構建系統有自己的生命周期,初始化、配置和運行三個階段。
- 初始化階段,會去讀取根工程中setting.gradle中的include信息,決定有哪幾個工程加入構建,創建project實例,比如下面有三個工程:include ':app', ':lib1', ':lib2'
- 配置階段,會去執行所有工程的build.gradle腳本,配置project對象,一個對象由多個任務組成,此階段也會去創建、配置task及相關信息。
- 運行階段,根據gradle命令傳遞過來的task名稱,執行相關依賴任務。 </ol>
任務創建
很多文章都會告訴你,任務創建要這樣:
task hello { doLast { println "hello" } }
或者用<<替換doLast,那我就很納悶,定義個任務怎么這么麻煩,還要加什么doLast,我直接這樣不行嗎?
task hello { println "hello" }
上面的這種寫法,“hello” 是在gradle的配置階段打印出來的,而前面的寫法是在gradle的運行階段打印出來的,所以怎么寫要看你的需求了。
另外task中有一個action list,task運行時會順序執行action list中的action,doLast或者doFirst后面跟的閉包就是一個action,doLast是把action插入到list的最后面,而doFirst是把action插入到list的最前面。
任務依賴
當我們在Android工程中執行./gradlew build的時候,會有很多任務運行,因為build任務依賴了很多任務,要先執行依賴任務才能運行當前任務。任務依賴主要使用dependsOn方法,如下所示:
task A << {println 'Hello from A'} task B << {println 'Hello from B'} task C << {println 'Hello from C'} B.dependsOn A C.dependsOn B
了解更多,可以看一下偵躍翻譯的Gradle tip #3-Task順序。
增量構建
你在執行gradle命令的時候,是不是經常看到有些任務后面跟著[UP-TO-DATE],這是怎么回事?
在Gradle中,每一個task都有inputs和outputs,如果在執行一個Task時,如果它的輸入和輸出與前一次執行時沒有發生變化,那么Gradle便會認為該Task是最新的,因此Gradle將不予執行,這就是增量構建的概念。
一個task的inputs和outputs可以是一個或多個文件,可以是文件夾,還可以是project的某個property,甚至可以是某個閉包所定義的條件。自定義task默認每次執行,但通過指定inputs和outputs,可以達到增量構建的效果。
依賴傳遞
Gradle默認支持傳遞性依賴,比如當前工程依賴包A,包A依賴包B,那么當前工程會自動依賴包B。同時,Gradle支持排除和關閉依賴性傳遞。
之前引入遠程AAR,一般會這樣寫:
compile 'com.somepackage:LIBRARY_NAME:1.0.0@aar'
上面的寫法會關閉依賴性傳遞,所以有時候可能就會出問題,為什么呢?本來以為@aar是指定下載的格式,但其實不然,遠程倉庫文件下載格式應該是由pom文件中packaging屬性決定的,@符號的真正作用是Artifact only notation,也就是只下載文件本身,不下載依賴,相當于變相的關閉了依賴傳遞,可以看一下sf的這個問題,通過添加transitive=true可以解決。但其實如果遠程倉庫有pom文件存在,compile后面根本不需要加"@aar",也就不會遇到這個問題了。
來自:http://www.csdn.net/article/2015-08-10/2825420
</div>
涉及到的知識點和內容比較多,我不會一一講解,本文主要會解答自己學習過程中的一些疑問,講解一些相關概念和實戰經驗,過程中也會推薦一些有質量的博客文章。
Groovy語言
Gradle基于Groovy語言,雖然接觸Gradle比較久,甚至寫過一點Groovy語句,但對語言本身并不了解。為什么用Groovy呢?Groovy運行在JVM上,在Java語言的基礎上,借鑒了腳本語言的諸多特性,相比Java代碼量更少,Groovy兼容Java,可以使用Groovy和Java混合編程,可以直接使用各種Java類庫。
Groovy語法的學習,推薦官方文章Differences with Java和IBM developerWorks的精通Groovy。了解了基本語法,對讀寫gradle腳本都會有幫助,比如隨便舉下面幾個例子: