抓重點學Gradle

fnxz3135 8年前發布 | 31K 次閱讀 Gradle 項目構建

前言

網上關于Gradle的教程很多,但很多都是以“面”切入— 通過大量講解其用法及其API分類來闡述。但Gradle API使用技巧眾多,API更是成千上百,臣妾記不住呀。個人深信,技術之道與練功之道是一致的,"想要曠世武功,先練內功"。本文嘗試以“點”切入,闡述一下學習Gradle的門路。

為什么使用Gradle

所謂構建,簡單來說就是根據不同的輸入信息,執行不同的任務,得到不同的輸出的過程。而構建工具就是把這些過程自動化、管理執行不同的任務來滿足不同需求。常見的構建工具有Ant、maven等。但它們都有一些缺點:

  • Ant的缺點

    依賴管理較弱,很容易變臃腫, XML作為構建腳本

  • Maven的缺點

    比較精簡,但限制性太多,編寫插件擴展麻煩,XML作為構建腳本。

而Gradle可以說是集大成者,比Ant 語法精簡, 比Maven靈活,拋棄了XML的繁瑣配置,極其強大的依賴管理,基于Groovy動態語言而使得自定義行為十分方便。

關于為什么使用Gradle這個問題,官方也作出了解釋:

why-gradle

簡單翻譯總結,就是使用Gradle很方便很靈活。

好吧,前面的都是“廢話”,作為Android開發者,你不用Gradle編譯試試...

Gradle

要學好Gradle,最重要的是學會 查文檔、查文檔、查文檔 ...重要的事情說三遍!

Groovy

由于Gradle是基于Groovy,所以要學習Gradle,必須要掌握一定的Groovy基礎。

Groovy是什么

簡單來說,Groovy是拓展了Java語言的一種動態語言,可以作為Java平臺的腳本語言使用,擁有類似 PythonRubySmalltalk 中的一些特性。

Groovy學習

當然,第一步肯定是配置環境,關于Groovy的環境搭建,具體可以參見 Groovy官網 ,特別簡單,不在此重復說明。然后,創建一個test.groovy文件,里邊只有一行代碼:

println  "hello groovy"

運行一下:

hello

是不是感覺和python好類似

作為一門語言,Groovy是復雜的。要深入研究的話,不會比學任一語言要簡單。但是作為一名普通開發者,個人覺得掌握其中一點— Closure(閉包) ,足矣。

  • Groovy基礎

    為啥這么說,首先,Groovy是完全兼容Java語法的,在學習前期,你完全可以使用Java語法代替Groovy語法編寫Groovy程序,畢竟是Groovy程序,隨著后期對于Groovy越來越熟悉,自然而然就會轉回更舒服的Groovy語法;其次,Groovy語法真心不難,舉幾個常見的特殊語法:

    • Groovy中支持動態類型,即 定義變量的時候可以不指定其類型,同一個變量可以反復賦值,而且可以是不同類型的變量 。這是動態語言和靜態語言比較重要的區別

      譬如,我們把上面的test.groovy修改為:

      def test=2
      test='tt'
      println  "${test}"

      運行一下:

      tt

    但是這種語法早在別的動態語言,如Python、Ruby中出現過了,估計不知道的程序員幾乎沒有吧。

    • 在上面例子中,最后輸出的語句 println "${test}" 其實包含了Groovy兩個語法糖

      • Groovy中函數調用的時候還可以不加括號
      • 如果字符中有$號的話,則它會把 $表達式 先求值

      其實上述例子完全可以寫成這樣:

      def test=2
      test='tt'
      //println  "${test}"
      println(test)
      </li> </ul>

      還有一些別的語法,如 可以不用分號結尾 、 函數的返回值也可以是無類型 等,但這些稍微查一下 GroovyAPI文檔 或google一下就懂了,沒啥技術門檻。

      ?

      </li>
    • Closure(閉包)

      說了這么多,是時候到主角— Closure(閉包) 出場了。 Closure(閉包) 是Groovy中函數式編程的最好體現。所謂 函數式編程 跟我們熟知的 面向過程編程 、 面向對象編程 一樣也是一種 "編程范式" ,也就是如何編寫程序的方法論。函數式編程最鮮明的特點是 函數是“第一等公民“ ,所謂 "第一等公民" ,指的是函數與其他數據類型一樣,處于平等地位,可以賦值給其他變量,也可以作為參數,傳入另一個函數,或者作為別的函數的返回值。

      在Groovy中,閉包表示一段可以執行的代碼塊,定義如下:

      { [closureParameters -> ] statements }

      其中,閉包必須使用花括號括起來,[closureParameters -> ]表示閉包參數列表,可以為空,但其為空時,閉包默認會隱含一個參數it,statements是閉包需要執行的代碼,最后一行代碼是返回值。

      定義好閉包后,可以通過 closure(參數) 或者 closure.call(參數) 調用

      舉個例子:

      def code={123}
      println  code.call(1)
      println  code()
      輸出:
      123
      123

      def add = { int x,y-> x+y } println add(3,4) 輸出: 7

      def isEven = { it%2 == 0 }
      println isEven(7) 輸出: false</pre>

      Closure使用注意點

      • 省略圓括號

        在Groovy中我們要遍歷一個List,一般做法是這樣的:

        def list = [1,2,3,4,5] 
        list.each{  
          println it
        }

      我們查一下它的API文檔,是這樣的

      method

      What the hell!!!完全跟上面寫法對應不上呀,實際上是因為,當函數的最后一個參數是閉包的話,可以省略圓括號,所以上面完整寫法應該是這樣的:

      def list = [1,2,3,4,5] 
      list.each({
        println it
      })

      還是不對呀,明明是兩個參數呀,怎么只傳了一個。讓我們查一下文檔,這個方法定義在 DefaultGroovyMethods 中,看一下它的類說明:

      This class defines new groovy methods which appear on normal JDK classes inside the Groovy environment. Static methods are used with the first parameter being the destination class, i.e. public static String reverse(String self) provides a reverse() method for String.

      恍然大悟,原來這個類定義了Groovy環境下一些類支持的方法,而其中 定義的靜態方法第一個參數僅僅是為了標示目標類 ,所以是不需要傳遞的。

      </li> </ul>

      Gradle

      Gradle是什么

      ? Gradle是基于Groovy定義了一套DSL,所謂DSL(領域專用語言),就是專門針對某一特定問題的計算機語言。而Gradle我們可以認為是經過“定制”的Groovy,專門用于項目構建的語言。

      Gradle學習— TaskProject , Plugin

      雖然使用Android Studio開發的話,Gradle環境在IDE會自動下載集成,但要學好Gradle,建議還是自己部署一下環境。從 Gradle官網 下載gradle后,配置一下環境變量。Gradle的環境就搭建起來了。

      和Groovy同理,要深入Gradle的話,要學的東西也不會少,但是作為普通的開發者,個人覺得把三個概念搞懂就可以了: TaskProject , Plugin

      • Project

        在Gradle中,每一個project,Gradle都會創建一個Project對象,并將這個對象與構建腳本相關聯。也就是說, Project對象與 build.gradle 是一對一的關系 ,所以你在 build.gradle 寫的每一個配置其實就是它對應的 Project 對象的一個方法或者一個變量值,譬如說我們配置項目依賴:

        dependencies {
                classpath 'com.android.tools.build:gradle:1.5.0'
            }

        它其實對應Project對象 void dependencies(Closure configureClosure); 方法。

        從面向對象的角度去理解Gradle,是不是覺得容易多了。

      • Task

        Task表示一些需要執行的構建任務,比如說lint檢查任務。定義一個Task可以這樣寫:

        task hello << {
            println "hello"
        }

        看起來挺莫名其妙的吧,還是從面向對象角度去把上面代碼還原一下:

        task("hello").leftShift({
           println "hello"
        })
        1. 定義一個task相當于調用了Project對象的task方法
        2. 任務名是task方法的參數名
        3. << 是Groovy的 運算符重載 ,在Groovy中,其實就是 leftShift 方法, leftShift 方法 等價于 doLast , doLast 是gradle提供訪問task任務的一個API,類似的還有 doFirst ,當一個task被執行的時候,可以通過 doFirst 和 doLast 向task中動態添加操作。 doFirst / doLast 會在task本身被執行之前/之后才會被執行
        4. leftShift 方法接收的參數是一個 Closure
        5. </ol>

          從上述分析可知,其實 一個Task就是一個標準的Groovy函數調用

          </li>
        6. Plugin

          Gradle是一個框架,作為框架,它負責定義流程和規則。而具體的編譯工作則是通過插件的方式來完成的。比如 編譯Java有Java插件,編譯Groovy有Groovy插件,編譯Android APP有Android APP插件,編譯Android Library有Android Library插件 。簡單來說,插件就是一系列任務的集合,主要作用是把一些重復利用的邏輯打包,這樣就可以在不同的項目中可以重復的使用。要應用插件,可以通過引入依賴的方式添加,舉個例子,要引入Android APP插件,就需要在build.gradle引用Android APP插件:

          buildscript {
              repositories {
                  jcenter()//表示編譯過程中依賴的倉庫
              }
              dependencies {
                //依賴android開發的gradle插件,groupId:artifactId:version
                  classpath 'com.android.tools.build:gradle:1.5.0'
              }
          }
          //應用插件
          apply plugin: 'com.android.application'

          //配置插件屬性 android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.test" versionCode 1 versionName "1.0.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }</pre> </li> </ul>

          Gradle進階— 自定義插件

          ? Gradle允許開發者自定義插件來實現項目一些特殊的構建需求。譬如說:動態注入代碼。我們可以直接在build.gradle定義自己的插件,但一般很少這么干,畢竟插件更多是為了項目復用的。更多情況,Gradle插件是以獨立插件工程的形式開發的。下面以一個簡單例子來介紹一下開發一個獨立Gradle插件基本步驟:

          假設現在我們開發一個直接輸出輸入內容的插件:

          1. 建立插件工程

            由于Gradle是基于Groovy開發的,所以我們建立的必然是Groovy工程,項目目錄結構如下

            plugin

            build.gradle如下:

            apply plugin: 'groovy'
            apply plugin: 'maven'
            version = '1.0.0'//插件version
            group = 'com.adison.gradleplugin'//插件groupId
            archivesBaseName = 'TestPlugin'//插件artifactId

            dependencies { compile gradleApi()//gradle sdk compile localGroovy()//Groovy sdk }

            //發布配置 uploadArchives { repositories.mavenDeployer { //發布到本地 repository(url: 'file:repo/') } }</pre> </li>

          2. 編寫插件

            • 實現Plugin接口

              package com.test
              import org.gradle.api.Plugin
              import org.gradle.api.Project
              class TestPlugin implements Plugin<Project> {
                  @Override
                  void apply(Project target) {
                  }
              }

              這是插件的入口類

            • 定義輸入信息擴展類

              package com.test
              class TestExtension {
                  String message;
              }
            • 定義插件id

              Gradle定義插件id需要建立這樣一個目錄結構: /resources/META-INF/gradle-plugins ,然后在其底下創建一個properties文件: print-message.properties 文件名即是插件id,也是 apply plugin: 'xxx' 使用的名字,在文件中輸入插件入口類:

              implementation-class=com.test.TestPlugin
            • 完善插件

              package com.test

              import org.gradle.api.Plugin import org.gradle.api.Project

              class TestPlugin implements Plugin<Project> {

              public static final String EXTENSION_NAME = "printMessage";//擴展別名
              
              @Override
              void apply(Project target) {
                //創建擴展
              

              TestExtension extension= target.extensions.create(EXTENSION_NAME,TestExtension) target.task("printMessage")<< { println extension.message } } }</pre>

              ?

              </li> </ul> </li>
            • 發布插件

              命令行執行:

              ./gradlew clean uploadArchives

              會在當前repo目錄生成插件文件

            • 插件使用

              • 插件文件導入項目,譬如導入項 根目錄/gradleplugin

              • 引入本地倉庫

                buildscript {
                    repositories {
                        jcenter()
                        mavenCentral()
                        //本地倉庫
                        maven {
                            url 'gradleplugin'
                        }
                    }
                    dependencies {
                        classpath 'com.android.tools.build:gradle:1.5.0'
                           //自定義插件
                        classpath 'com.adison.gradleplugin:TestPlugin:1.0.0'
                    }
                }
              • 應用插件

                apply plugin: 'print-message'
                printMessage{
                    message="test"
                }
                命令行輸入:
                ./gradlew printMessage
                輸出:
                test
              • </ul>

                舉這個列子只是為了描述了一下自定義Gradle插件的流程,結合Groovy和Gradle內建的Task,可以實現更復雜實用的插件。

                </li> </ol>

                Android Gradle使用

                從上述可以得知,Android其實就是寫了兩個插件: com.android.application 和 com.android.library ,應用這兩個插件就可以實現Android APP和Android Library的構建了,所以也沒啥好說的,不知道怎么配置?查文檔: Gradle Plugin User Guide

                這里簡單分享幾個有用的經驗:

                • 自定義模塊結構

                  項目還是是Eclipse結構,想保留原有目錄結構的同時導入到AS開發,可以自己手動生成build.gradle,配置項目目錄結構:

                  android {
                       ....
                      sourceSets {
                          main {
                              manifest.srcFile 'AndroidManifest.xml'
                              java.srcDirs = ['src']
                              resources.srcDirs = ['src']
                              aidl.srcDirs = ['src']
                              renderscript.srcDirs = ['src']
                              res.srcDirs = ['res']
                              assets.srcDirs = ['assets']
                              jniLibs.srcDirs = ['libs']
                          }
                      }
                  }
                • 由于項目協作, 以及跨平臺編譯, 在任何時候, 你都應該使用 Gradle Wrapper 而非本機安裝Gradle

                  • Gradle Wrapper是Gradle的封裝。即便你的機器上沒有安裝Gradle,使用Gradle Wrapper也可以執行Gradle的構建工作。
                  • Gradle Wrapper在項目新創建是會自動生成,使用時會自動下載對應版本的Gradle。
                  • 版本一致可以避免很多因為協作或跨平臺的問題。

                  Gradle Wrapper使用很簡單:

                  </li> </ul>
                  使用gradle:
                    gradle tasks
                  使用gradle wrapper
                    ./gradlew tasks
                  • Build Variants(構建變種版本)

                    Build Type + Product Flavor = Build Variant。

                    Android通過Build Variants可以很方便構建不同的版本,滿足各種版本需求。

                    • Build Type: 構建類型,如debug,release,一般用于構建不同類型提供團隊內部使用
                    • Product Flavor : 定制類型,一般用于區分渠道打包
                    android {
                        ...
                        buildTypes {
                            debug{
                              ...
                            }
                            release {
                               ...
                            }
                        }
                        productFlavors {
                            flavor1 {
                              ...
                            }
                            flavor2 {
                             ...
                            }
                        }
                    }

                    如上面配置,則會生成4個 Build Variant

                    • Flavor1 - debug
                    • Flavor1 - release
                    • Flavor2 - debug
                    • Flavor2 - release
                    </li> </ul>

                    小結

                    至此,相信大家對gradle已經有一定認識。我在上述并沒有對一些細節展開論述,而僅僅對我個人認為比較重要的點進行了闡述,是因為我覺得對于Gradle的學習,掌握理解其重要的概念遠比記住其API重要。萬變不離其宗,掌握了“點”,“面”只是時間的問題。最后,再重申一句, 學會查文檔真很重要

                     

                    來自:http://www.jianshu.com/p/cd1426ec875b

                     

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