1. 程式人生 > >讀書筆記--Android Gradle權威指南(上)

讀書筆記--Android Gradle權威指南(上)

說了 聲明 通過命令 google robot 結構 信息 我只 cati

最近看了一本書《Android Gradle 權威指南》,對於 Gradle 理解又更深了,但不想過段時間就又忘光了,所以打算寫一篇讀書筆記,將書中一些我個人覺得蠻有用的點記錄、總結一下。

前言

首先,先來過一下整書的目錄章節,先大概清楚整本書都介紹了哪些知識點:

第 1 章 Gradle 入門

第 2 章 Groovy 基礎

第 3 章 Gradle 構建腳本基礎

第 4 章 Gradle 任務

第 5 章 Gradle 插件

第 6 章 Java Gradle 插件

第 7 章 Android Gradle 插件

第 8 章 自定義 Android Gradle 工程

第 9 章 Android Gradle 高級自定義

第 10 章 Android Gradle 多項目構建

第 11 章 Android Gradle 多渠道構建

第 12 章 Android Gradle 測試

第 13 章 Android Gradle NDK 支持

第 14 章 Android Gradle 持續集成

整本書介紹的內容很全,從 Gradle 的環境配置 --> Groovy 介紹 --> 講解項目中常見 gradle 文件作用 (setting.gradle, build.gradle) --> 詳細講解 build.gradle 文件內每行代碼的含義 --> 各種高級自定義使用。

看完這本書,對於掌握項目中的 build.gradle 文件應該就不成問題了,雖然將整本書過了一遍,但其實我也只是著重挑了一些自己感興趣的章節深入閱讀,所以就來記錄一下,方便後續查閱吧。

筆記

1. Groovy 基礎

首先清楚一點,Gradle 是基於 Groovy 語言的,他們之間的關系就像《Android 群英傳:神兵利器》中說的:

Groovy 對於 Gradle,就好比 Java 對於 Android

所以,了解一些 Groovy,對於學習 Gradle 來說,肯定是有所幫助的。

關於這方面內容,我之前寫過一篇博客:學點Groovy來理解build.gradle代碼

所以,這裏不會再去介紹,但有幾個點可以提一下,如果你都還不怎麽熟悉,那麽可以點開鏈接去看看:

  • Groovy 中支持用 ‘xxx‘,"xxx",‘‘‘xxx‘‘‘,/xxx/ 等多種方式來定義字符串,所以如果在 build.gradle 裏看到既有單引號又有雙引號定義的字符串時,不用去疑惑他們到底是不是字符串。
  • Groovy 中的方法支持省略括號,也就是說,在 build.gradle 中一行行的代碼,大部分都是在調用某個方法。
  • Groovy 中有一種特性叫閉包,說白點也就是代碼塊,支持作為方法參數,結合方法括號省略的特點,在 build.gradle 裏 defauleConfig {} 代碼塊之類的其實也都是在調用一個個方法。

2. Android 項目中的 Gradle

技術分享圖片

新建一個項目時,Android Studio 會自動生成項目的初步結構,這通常會攜帶一些 gradle 相關的文件,這一節就來學學,各個 gradle 文件都有什麽作用

2.1 gradle/wrapper 目錄

就像我們要開發 Java 程序,本地需要配置 JDK 環境,要開發 Android 程序,需要配置 SDK 一樣,想要借助 Gradle 來構建項目,那麽按理說本地也需要配置相關的 Gradle 環境才對。

而我們之所以可以省掉這一步,就是 gradle/wrapper 這個目錄下的文件的作用了,可以先看看 gradle-wrapper.properties 這個文件的內容:

#Thu May 24 10:30:42 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip

內容無非就是一些配置項,而最重要的就是最後一句,指明了當前這個項目要使用哪個版本的 Gradle 來構建,我們在 Android Studio 的 File -> Project Structure -> Project 裏配置的 Gradle Version,最終改變的其實就是上述文件裏最後一行的 Gradle 版本屬性值

技術分享圖片

官方說了,提供了 gradle/wrapper 這種方式,可以讓你特別靈活的進行配置,想換個 Gradle 版本來構建項目,只需要修改這個配置文件的 Gradle 版本屬性值即可,當然也可以直接通過 AS 提供的 UI 界面操作,結果都一樣。

由於 Gradle 更新換代特別快,而且新的大版本經常都會提供很多新特性,這就導致了在 clone Github 上一些開源項目到本地構建時經常有報錯的問題,本質原因就是因為它使用的 Gradle 版本跟你本地不一樣,而由於有堵巨墻的原因,導致一直沒法成功下載它配置的 Gradle 版本,進而就無法構建項目,而報錯了。

網上說的一些解決方案是讓你手動去修改 gradle-wrapper.properties 文件裏的 Gradle 版本,改成你本地的版本,但我覺得這種方法不一定適用,這取決於那個項目中是否有用到一些新特性,以及你本地的 Gradle 版本是否兼容項目中用到的 Gradle 新特性。

通常來說,如果你本地的 Gradle 比克隆的項目的 Gradle 版本高的話,那麽這種直接修改項目的 Gradle 版本方式應該是可行的,那麽怎麽知道你本地都有哪些 Gradle 版本呢:

技術分享圖片

默認在 C 盤, C:\Users\suxq .gradle 目錄下有 Android Studio 自動幫你下載的 Gradle 的各個版本,只要你在 gradle-wrapper.properties 修改了 Gradle 的版本號,那麽當你在構建項目時,就會先到你電腦的這個路徑下查找相對應版本的 Gradle,如果可用,則直接進行構建項目任務,如果不存在,那麽就會自動去下載對應版本的 Gradle。

最後,還有個問題,怎麽確定都有哪些 Gradle 版本可用呢?如果想要自己去下載,不借助 Android Studio 可行麽?

當然可以,去官網找到對應版本點擊下載即可:http://services.gradle.org/distributions/

下載完成之後,將文件放到上面介紹的 C 盤下的 .gradle 文件裏相對應版本的目錄下即可。

如果你有自己去嘗試下載,你就會體驗到,下載速度是有多麽的龜速,90M 左右的文件硬是要下載個把小時。同樣的道理,你自己下載這麽慢,那通過 Android Studio 下載的速度也同樣這麽慢,兩者唯一的區別就只是在於你自己下載時你可以看到速度和進度。

這樣一來的話,明白為什麽有時候打開新項目或者打開 Github 上的項目時,Android Studio 會一直卡在構建中的原因了吧?

因為這個項目用到了你本地沒有的 Gradle 版本,所以 Android Studio 自動去下載了,但由於都懂的原因,下載速度賊慢,因此網上才有一些博客教你說讓你要去FQ,明白為什麽了吧。

另外,網上還有一些博客會讓你不管它,讓你等隔天再去打開這個項目,然後有時候你會發現,隔天打開竟然能正常構建項目了,莫名其妙的就好了,就不一直卡在構建中了。這是由於 Android Studio 會有一個後臺構建的功能,也就是說它可以在背後默默的幫你自動去下載 Gradle,雖然速度賊慢,但總有下載完成的時候,當你隔天再去打開這個新項目時,如果已經下載好了,那自然就可以正常構建使用了。

2.2 gradlew.bat 文件

gradlew 文件和 gradlew.bat 文件,兩份沒有什麽差別,它們都是腳本文件,區別只是一個是 shell 腳本,一個是批處理腳本,那麽自然一個是用來在 Linux 上運行,一個在 Windows 上運行。

感興趣的可以去看看這份腳本代碼,其中比較關鍵的代碼:

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

大概翻譯一下,它借助了 gradle/wrapper 目錄下的 gradle-wrapper.jar 文件,並借助了 java 命令,提供了可讓我們直接以命令行形式運行一些相應的 gradle 指令,而這些指令在 gradle-wrapper.jar 文件中都提供了相應的實現。

比如: gradlew -version

技術分享圖片

直接在 Android Studio 的 Terminal 面板運行 gradlew -version 命令,或者在 Dos 窗口,進到項目的根目錄下執行該命令,都可以,前者只是打開時就默認將項目根目錄作為當前路徑了。

這也是為什麽一些資料說,如果沒有配置 Gradle 環境,那麽在每個項目根目錄下也可以運行 gradle 命令的原因,因為每個項目都提供了 gradlew.bat 腳本以及 gradle/wrapper 目錄下的 gradle-wrapper.jar 文件支持。你可以試一下,刪掉兩者中任意一個,就沒法正常運行 gradle 命令了。

那麽,提供了腳本文件來支持直接運行 gradle 命令有什麽用呢?

用處可多了,我們在構建項目時,基本都是直接借助 Android Studio 的圖形界面來操作,點一點就可以了。但有時候,經常會遇見一些構建失敗的情況,然後日誌中經常會給我們這麽一段提示:

技術分享圖片

如果想查看更多的日誌信息,需要在執行命令的時候加上一些參數,而這種時候就需要通過命令行的形式來了,那麽這時腳本文件也就派上用場了:

技術分享圖片

技術分享圖片

這樣一來就可以看到更多的日誌信息了,當然我舉的這個例子不好,因為可以直接看出問題出在哪了,不需要再去獲取更多的輔助信息來定位了。

但有些時候,Gradle 構建時確實就是失敗了,然後給的信息又少,讓人莫名其妙,不知道為啥失敗了,這種時候就可以借助命令行形式來執行這個 task,然後添加一些參數來獲取更多的輔助日誌。至於要添加哪些參數,執行什麽命令,通常情況下,Gradle 構建失敗時都會給出建議,跟著來就可以了。

2.3 setting.gradle 文件

setting.gradle 文件通常是當項目涉及到多 Module 的場景。

技術分享圖片

只有在 setting.gradle 中 include 的 Module,才會被加入構建中,也就是說,如果一個項目文件夾內,包含了很多子工程,但如果沒在 setting.gradle 中將這些子工程 include 進來的話, 這些 Module 是不會參與進構建的。

另外,如果子工程的這些 Module 都直接放在了項目根目錄中,那麽 setting.gradle 中只需要寫 include 就可以了,那如果這些子工程是放在別的地方,那麽也可以通過修改 project().projectDir 來指定子工程的具體路徑,也就是說,所有的 Module 並不一定需要全部集中放在同一個項目內。

2.4 build.gradle 文件

一個項目中可能存在多個子工程,每個子工程構建都應該是相互獨立的,也就是說,每個子工程都可以根據自己的需要,配置各種依賴,插件等。那麽,Gradle 是如何分開來管理每個子工程的構建任務的呢?

這就是 build.gradle 文件的作用了,所以你會發現,每個子工程,也就是每個 Module 都會有一個 build.gradle 文件,Gradle 就是以這個文件為根據來構建這個 Module。

那麽,如果有些配置項,在所有的子工程中都是一致的話,如果在每個子工程裏都去重復粘貼的話,當這個共同的配置項需要發生變化時,維護起來會非常麻煩,這也就是為什麽根目錄下面還會有一個 build.gradle 文件。

根目錄下的這個 build.gradle 是統籌全局的,在這裏,你可以配置一些所有工程共同的配置項,比如 Android Gradle 的版本,依賴庫的倉庫地址這些所有工程的共同配置項。

也就是說,其實將根目錄下的 build.gradle 文件裏的內容移到每一個工程下的 build.gradle 裏,也是可行的。但沒必要這樣做,吃飽了撐著。

3. Gradle 基礎

3.1 task 概念

task 是 Gradle 中的一種概念,引用書中的解釋:

一個 task 其實就是一個操作,一個原子性的操作,比如打個 jar 包,復制一份文件,編譯一次 Java 代碼,上傳一個 jar 到 Maven 中心庫等,這就是一個 Task,和 Ant 裏的 Target, Maven 中的 goal 是一樣的。

有點類似於 Java 裏面的類,但又有很多不同之處。我們要通過 Java 命令來執行某個 java 文件,那麽至少需要一個類,類裏面需要有 main 方法,這個 java 文件才能運行起來。

同樣,要通過 gradle 命令來執行某個構建任務,那麽至少需要一個 task,這個構建任務才能跑起來。

但更多的是不同的概念,多個類之間可以有相互依賴的關系,類中持有另一個類的引用等等。

但在 task 方面,多個 task 之間只能有前後依賴關系,即某個 task 的運行是否需要哪個 task 先運行的基礎上才允許,也就是說,在 Gradle 的構建工作過程中,多個 task 是構成一條直線的,一個個 task 按順序的去工作,而不存在某個 task 工作到一半時去調用另一個 task。

不過,通常情況下,我們並不需要去接觸到 task 層面,build.gradle 文件裏的代碼大多都只是在調用各種方法進行各種配置,而最後,會根據這份文件生成很多 task,比如:

技術分享圖片

在 Android Studio 右側的 Gradle 的面板這邊,就是一個個的 task,assemble 是一個 task,build 也是一個 task,很多 task 是 Gradle 已經提供的,而有些 task 則是根據 build.gradle 裏面的配置項自動生成的,比如 assembleDebug 這一類。

要執行 task 的方式,可以通過 AS 的圖形界面點一點即可,也可以通過命令行方式,由於根目錄提供了 gradlew 腳本文件,因此可以在根目錄下執行 gradlew task名 即可。

3.2 gradle 插件概念

Gradle 是用來構建項目的,但並不是說只能用於構建 Android 的項目,Java 的也行,C++ 的也行,很多很多。

那如果我只是做 Android 開發,我也就只需要 Gradle 構建 Android 項目的功能即可,其他的又不需要,鑒於此,Gradle 封裝好了基本的構建工作,然後提供了插件的接口,支持根據各自需要去擴展相應的構建任務。

以上就是我對於 Gradle 插件概念的理解,我認為它是用於給大夥可以根據需要自行去擴展。

就拿 Android 項目來說,來看一份 build.gradle 文件結構:

apply plugin: ‘com.android.application‘

android {
    ...
    defaultConfig {
        ....
    }
}

dependencies {
    ...
}

如果有點擊方法進去看過源碼的話,你會發現:

技術分享圖片

技術分享圖片

發現沒有,dependencies 是 Gradle 提供的方法,但 android,defaultConfig 卻都是 Android Gradle 插件提供的方法了。

也就是說,其實 Gradle 只提供了構建項目的一些基本功能,如配置依賴庫,不管什麽項目都需要。但像 android {} 代碼塊裏的配置項,很明顯,就只有 Android 項目才需要用到了,所以這些配置並不是由 Gradle 來提供的,而是由 Android Gradle 插件提供的,這也就是為什麽在根目錄的 build.gradle 文件裏會有這麽一行代碼:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath ‘com.android.tools.build:gradle:2.3.3‘
    }
}

com.android.tools.build:gradle:2.3.3 這行代碼其實就是聲明了我們的項目還需要使用 Android Gradle 插件,版本號為 2.3.3,而插件的下載地址在 jecnter() 倉庫。

所以,得搞清楚,Gradle 和 Android Gradle 是兩種概念,也是兩個不同的東西,如果有人問說你項目構建的 Gradle 的版本是多少,得搞清楚,他想問的是 Gradle,還是 Android Gradle 插件。

但是我們在根目錄的 build.gradle 裏是可以配置多個插件的,比如如果有使用 GreenDao,或者使用了 Jcenter 的上傳功能:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        //Google提供的構建Android項目的插件
        classpath ‘com.android.tools.build:gradle:2.3.3‘
        //GreenDao 提供的插件
        classpath ‘org.greenrobot:greendao-gradle-plugin:3.2.2‘
        //Jcenter提供的插件
        classpath ‘com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4‘
        //Maven提供的插件
        classpath ‘com.github.dcendents:android-maven-gradle-plugin:1.4.1‘
    }
}

那麽,Gradle 在根據 build.gradle 構建 Module 時,怎麽知道要使用哪個插件呢,聲明了這麽多。

這就是為什麽在每個 Module 的 build.gradle 文件的開頭有行 apply plugin 的代碼了。

apply 是 Gradle 的方法,它可以接收一個 map 類型的參數,而在 Groovy 中,map 的定義可以直接 ‘key‘: value,也就是說:

apply plugin: ‘com.android.application‘  
//等效於
// def map = [‘plugin‘:‘com.android.application‘]
// apply(map)

每個 build.gradle 開頭這行代碼,其實是調用了 Gradle 的 apply() 方法,然後傳入了一個 map 值,key 為 plugin, value 為 ‘com.android.application‘,那麽 Gradle 就知道了你這個項目需要使用到一個 id 為 ‘com.android.application‘ 的插件來輔助構建了,那麽它就會去你在根目錄下配置的插件列表裏尋找。

也就是說,apply plugin 是 Gradle 規定並提供的,但 ‘com.android.application‘ 則是由 Android Gradle 來提供的。

那麽,小結一下,要使用一個 Gradle 插件的話,先得在根目錄下聲明你要用的插件以及版本,當然也得指定插件的下載地址,然後在你具體的 Module 的 build.gradle 的開頭通過 apply plugin 方式來應用插件,這個插件得有一個唯一區分開的 id 值。

4. 區分 Gradle 和 Android Gradle

技術分享圖片

先來看張圖,這個在 File -> Project Structure -> Project 打開,在這裏可以配置 Gradle 和 Android Gradle 插件的版本。

最開始接觸的時候,我看到這裏是有些迷茫的,怎麽有一個 Gradle 版本,又有一個 Android Gradle 版本。當別人問我你 Android Studio 使用的 Gradle 版本是多少時,我也傻乎乎的打開根目錄下的 build.gradle 文件裏,看到 com.android.tools.build:gradle:2.3.3,然後跟他說 2.3.3 版本,當初根本沒搞清楚這兩個有什麽區別,一直以為是同一個東西。

所以,要搞清楚 Android Gradle 其實只是 Gradle 的一個插件,是 Google 基於 Gradle 提供的插件接口所做的一些擴展。

所以,要查找 Android Gradle 的相關資料,自然就不是去 Gradle 官網了,而是要去 Android 官網找:

https://developer.android.google.cn/studio/releases/gradle-plugin

由於 Gradle 更新換代很快,又經常提供一些新特性,所以 Android Gradle 插件也就跟隨著發布了很多版本,所以,Android Gradle 的版本並不是可以任意更改的,因為它是基於每一個 Gradle 版本開發的,因此需要在對應的 Gradle 版本中才能使用。

這也是為什麽,我們有時候直接修改根目錄下的 build.gradle 中的 Android Gradle 版本時,會報一些錯誤說需要 Gradle 版本在多少在可以使用的原因,至於這些對應關系,官網當然有給出來了:

技術分享圖片

舉個例子,如果你 Gradle 版本使用的是 3.3,然後你在 Github 上 clone 了某個人的項目,他的項目中使用的 4.4 的 Gradle 版本 和 3.1.0 的 Android Gradle 插件,但是你發現在打開這個項目的時一直處於構建中,一直打不開。

你查了下原因,網上有教程說,讓你將項目中的 gradle/wrapper 文件裏的 Gradle 版本換成你本地項目中的 Gradle 版本,還跟你說因為它使用的版本你本地沒有,而且被墻了,你下載需要很長時間,讓你直接改成使用你本地的版本即可。

你改了後,發現,是可以打開項目了,但構建的時候又報錯了,說你使用了 3.1.0 的 Android Gradle 插件,需要讓你將 Gradle 版本改成 4.4 才可以正常構建,這 MMP 不是又繞回去了。

所以說,別聽他放屁,搞清楚了 Gradle 和 Android Gradle 插件的關系之後。那為什麽會一直在構建中,為什麽會報錯我們心裏就有數了,要解決,沒有其他辦法,就是要將對應的版本下載下來。

所以,你應該去搜的是如何下載,Android Gradle 插件並沒有被墻,如果想自行下載,可以參考我之前的博客: 如何用Android Studio查看build.gradle源碼,而至於 Gradle 要如何下載,如果官網下載不了,那就去搜搜有沒有相關的鏡像吧。


本篇就先到這裏了,還會有一篇下篇,下篇的內容就是側重於介紹 build.gradle 裏各種配置項的作用和意義了,還有就是如何自己寫 Gradle 腳本來運行,敬請期待~


技術分享圖片
最近(2018-03)剛開通了公眾號,想激勵自己堅持寫作下去,初期主要分享原創的Android或Android-Tv方面的小知識,準備可能還有點不足,感興趣的可以先點一波關註,謝謝支持~~

讀書筆記--Android Gradle權威指南(上)