Android項目中如何用好構建神器Gradle?

jopen 9年前發布 | 14K 次閱讀 Gradle Android開發 移動開發

摘要:本文作者賈吉鑫為大眾點評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. 實戰,實戰,再實戰。(三遍,你懂的)
  4. </ol>

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

    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.*
      </li>

    2. 經常看到${var1}的用法是怎么回事? 這是Groovy中的GString,可以在雙引號中直接使用,用于字符串疊加非常方便。

      def dx = tasks.findByName("dex${variant.name.capitalize()}")
      </li>

    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 -> }</pre></li> </ol>

      Gradle概念

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

      生命周期

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

      1. 初始化階段,會去讀取根工程中setting.gradle中的include信息,決定有哪幾個工程加入構建,創建project實例,比如下面有三個工程:include ':app', ':lib1', ':lib2'
      2. 配置階段,會去執行所有工程的build.gradle腳本,配置project對象,一個對象由多個任務組成,此階段也會去創建、配置task及相關信息。
      3. 運行階段,根據gradle命令傳遞過來的task名稱,執行相關依賴任務。
      4. </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>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!