Android專案中如何運用好Gradle?

csdn發表於2015-08-11

摘要:本文作者賈吉鑫為大眾點評Android工程師,在進行團隊並行開發時,分庫遇到的問題很多都要通過Gradle指令碼解決。Gradle雖為構建神器,但學習曲線比較陡峭,要想在Android專案中用好Gradle必須要做到三點。

最近在忙團隊並行開發的事情,主要是將各個團隊的程式碼分庫,一方面可以降低耦合,為後面模組外掛化做鋪墊,另一方面採用二進位制編譯,可以加快編譯速度。分庫遇到了一些問題,很多都要通過Gradle指令碼解決,所以稍微花時間研究了一下。

Gradle雖為構建神器,但感覺學習曲線比較陡峭。Gradle User Guide內容很多,但有點太多了,多的你看不完,Gradle Plugin User Guide一篇文章主要講了Android相關的配置,看完可能感覺馬馬虎虎會用,但到了修改一些構建流程的時候還是不知所措。經過一段時間的摸索,我覺得在Android專案中用好Gradle,你要做到以下三點:

  1. 瞭解Groovy基本語法。
  2. 粗讀Gradle User Guide和Gradle Plugin User Guide。
  3. 實戰,實戰,再實戰。(三遍,你懂的)

涉及到的知識點和內容比較多,我不會一一講解,本文主要會解答自己學習過程中的一些疑問,講解一些相關概念和實戰經驗,過程中也會推薦一些有質量的部落格文章。

Groovy語言

Gradle基於Groovy語言,雖然接觸Gradle比較久,甚至寫過一點Groovy語句,但對語言本身並不瞭解。為什麼用Groovy呢?Groovy執行在JVM上,在Java語言的基礎上,借鑑了指令碼語言的諸多特性,相比Java程式碼量更少,Groovy相容Java,可以使用Groovy和Java混合程式設計,可以直接使用各種Java類庫。

Groovy語法的學習,推薦官方文章Differences with Java和IBM developerWorks的精通Groovy。瞭解了基本語法,對讀寫gradle指令碼都會有幫助,比如隨便舉下面幾個例子:

  1. 比如為何在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.*
  2. 經常看到${var1}的用法是怎麼回事? 這是Groovy中的GString,可以在雙引號中直接使用,用於字串疊加非常方便。
    def dx = tasks.findByName("dex${variant.name.capitalize()}")
  3. 下面的程式碼你真的能看懂嗎?
    //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 ->
    }

Gradle概念

下面講幾個Gradle相關的概念,幾個比較重要的吧,更多的東西還是要自己去看Gradle User Guide。

生命週期

Gradle構建系統有自己的生命週期,初始化、配置和執行三個階段。

  1. 初始化階段,會去讀取根工程中setting.gradle中的include資訊,決定有哪幾個工程加入構建,建立project例項,比如下面有三個工程:include ‘:app’, ‘:lib1′, ‘:lib2′
  2. 配置階段,會去執行所有工程的build.gradle指令碼,配置project物件,一個物件由多個任務組成,此階段也會去建立、配置task及相關資訊。
  3. 執行階段,根據gradle命令傳遞過來的task名稱,執行相關依賴任務。

任務建立

很多文章都會告訴你,任務建立要這樣:

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”,也就不會遇到這個問題了。

相關文章