徹底弄明白Gradle相關配置

初識Gradle

Gradle是一個基於Apache Ant和Apache Maven概念的項目自動化建構工具。它使用一種基於Groovy的特定領域語言來聲明項目設置,而不是傳統的XML。當前其支持的語言限於Java、Groovy和Scala,計劃未來將支持更多的語言。

怎麼看上面都是一段很官方的解釋,對於入門的人來說簡直是一個噩夢般的解釋(包括以前的我)。那下面我就用通俗一點語言說說我的理解。

Gradle就是工程的管理,幫我們做了依賴,打包,部署,發佈,各種渠道的差異管理等工作。舉個例子形容,如果我是一個做大事的少爺平時管不了這麼多小事情,那Gradle就是一個貼心的祕書或者管家,把一些雜七雜八的小事情都幫我們做好了,讓我們可以安心的打代碼,其他事情可以交給管家管。

那有人會問,既然工作都可以交給他做,爲什麼還要我們去了解。我想我們要管家做事,也要下達我們的命令,我們必須知道這些命令和管家的喜好才能跟他相處和諧,不然你不知道它的脾性下錯命令,那後果可是很嚴重的。

在以前實習的時候,我還用eclipse,那是導入一個網上的下載的module還需要一步步的import。但自從用了Android Studio後,Gradle很貼心的幫我完成了這個繁雜的工作,而且往往只需要添加一句話,這太神奇了,當時我是這樣想的,下面我們也會說到這個。

分析

下面我就用自己項目中用到的Gradle慢慢分析: 
 
我們看到,每個Module都會對應有一個Gradle文件,另外還有一個主Project的Gradle文件管理全局。下面我們先看看那個叫gradle-wrapper.properties的文件:

gradle-wrapper

Wrapper是對Gradle的一層包裝,便於在團隊開發過程中統一Gradle構建的版本號,這樣大家都可以使用統一的Gradle版本進行構建。 
 
上面我們看到的圖就是Gradle提供內置的Wrapper task幫助我們自動生成Wrapper所需的目錄文件。再看看我們Android項目裏面自動生成的文件 
 
 
終於,我們知道這幾個自動生成的文件原來是Gradle Wrapper創建出來的。

那下面我們看看gradle-wrapper.properties這個文件的作用 
 
看到項目裏面的各個屬性,下面再看看每個屬性的作用 
 
我們其實最關心的應該是distributionUrl這個屬性,他是下載Gradle的路徑,它下載的東西會出現在以下的文件夾中 
 
看到了吧,這個文件夾包含了各個版本你下載的Gradle。

當我是初學者的時候老是會遇到一個問題,那就是下圖: 

導入項目的時候一直會停留在這個界面,這是爲什麼?其實原因很簡單,就是你常用項目的Gradle版本跟你新導入項目的Gradle版本不一致造成的,那怎麼解決?我本人自己是這麼做的:

  • 網速好或者科學上網的時候,由它自己去下載,不過下載時間有長有短,不能保證。
  • 當你在公司被限網速的時候,當然也是我最常用的,就是把你最近常用項目的gradle-wrapper.properties文件替換掉你要導入項目的該文件,基本上我是這樣解決的,當然有時候也會遇到替換掉報錯的情況,不過比較少。

settings.gradle

下面我們講講settings.gradle文件,它其實是用於初始化以及工程樹的配置的,放在根工程目錄下。

設置文件大多數的作用都是爲了配置自工程。在Gradle衆多工程是通過工程樹表示的,相當於我們在Android Studio看到的Project和Module概念一樣。根工程相當於Android Studio的Project,一個根工程可以有很多自工程,也就是很多Module,這樣就和Android Studio定義的Module概念對應上了。 
 
我們可以看到這個項目我們添加了7個module,一一對應,如果你的項目添加了項目依賴,那就會出現在這個文件當中。

好了,我們說完settings.gradle文件之後就慢慢進入其他文件了,但是首先我們要解釋一下什麼是Groovy:

Groovy

Groovy是基於JVM虛擬機的一種動態語言,它的語法和Java非常相似,由Java入門學習Groovy基本沒有障礙。Groovy完全兼容Java,又在此基礎上增加了很多動態類型和靈活的特性,比如支持密保,支持DSL,可以說它就是一門非常靈活的動態腳本語言。

一開始我總把Gradle和Groovy搞混了,現在我總把他們的關係弄清楚了。Gradle像是一個軟件,而Groovy就是寫這個軟件的語言,這就很簡單明瞭吧。那下面我們說到的內容都是用Groovy語法寫的,但是這個知識點我就暫時不科普了,有興趣的小夥伴可以去了解一下更深入的Groovy語法。

build.gradle(Project)

下面我們就來講講主的build.gradle文件: 

我們這裏,分爲四個標籤來講:

1.buildscript

buildscript中的聲明是gradle腳本自身需要使用的資源。可以聲明的資源包括依賴項、第三方插件、maven倉庫地址等

2.ext

ext是自定義屬性,現在很多人都喜歡把所有關於版本的信息都利用ext放在另一個自己新建的gradle文件中集中管理,下面我介紹一下ext是怎麼用的: 
 
- 首先我們新建兩個文件,分別叫build.gradle和version.gradle 
 
 
- 然後分別在兩個文件中打上相應的代碼 
 
- 最後在Android Studio的Terminal移動到相應的文件夾中運行task。 
我們可以很神奇的發現,當我們在build.gradle文件中輸入了apply from:’version.gradle’這句話,我們就可以讀取到該文件下ext的信息。

現在在項目中我也是這種方法統一管理所有第三方插件的版本號的,有興趣的朋友也可以試試。

3.repositories

顧名思義就是倉庫的意思啦,而jcenter()、maven()和google()就是託管第三方插件的平臺

4.dependencies

當然配置了倉庫還不夠,我們還需要在dependencies{}裏面的配置裏,把需要配置的依賴用classpath配置上,因爲這個dependencies在buildscript{}裏面,所以代表的是Gradle需要的插件。

下面我們再看看build.gradle(Project)的另一部分代碼 

  • allprojects

allprojects塊的repositories用於多項目構建,爲所有項目提供共同所需依賴包。而子項目可以配置自己的repositories以獲取自己獨需的依賴包。

奇怪,有人會問,爲什麼同一個build.gradle(Project)文件中buildscript和allprojects裏面的內容基本上是一樣的呢,他們的區別在哪?

  • buildscript和allprojects的作用和區別

buildscript中的聲明是gradle腳本自身需要使用的資源,就是說他是管家自己需要的資源,跟你這個大少爺其實並沒有什麼關係。而allprojects聲明的卻是你所有module所需要使用的資源,就是說如果大少爺你的每個module都需要用同一個第三庫的時候,你可以在 
allprojects裏面聲明。這下解釋應該可以明白了吧。

好了,下面該說說build.gradle(Project)文件的最後一個一段代碼了 
 
運行gradle clean時,執行此處定義的task。該任務繼承自Delete,刪除根目錄中的build目錄。相當於執行Delete.delete(rootProject.buildDir)。其實這個任務的執行就是可以刪除生成的Build文件的,跟Android Studio的clean是一個道理。

  • build.gradle(Module)

講完Project的build文件,就來講講最後也是內容最多的文件了。

  • apply plugin

首先要說下apply plugin:’×××’

這種叫做引入Gradle插件,而Gradle插件大致分爲分爲兩種:

  • apply plugin:’×××’:叫做二進制插件,二進制插件一般都是被打包在一個jar裏獨立發佈的,比如我們自定義的插件,再發布的時候我們也可以爲其指定plugin id,這個plugin id最好是一個全限定名稱,就像你的包名一樣;
  • apply from:’×××’:叫做應用腳本插件,其實這不能算一個插件,它只是一個腳本。應用腳本插件,其實就是把這個腳本加載進來,和二進制插件不同的是它使用的是from關鍵字.後面緊跟的坫一個腳本文件,可以是本地的,也可以是網絡存在的,如果是網絡上的話要使用HTTP URL. 
    雖然它不是一個真正的插件,但是不能忽視它的作用.它是腳本文件模塊化的基礎,我們 
    可以把龐大的腳本文件.進行分塊、分段整理.拆分成一個個共用、職責分明的文件,然後使 
    用apply from來引用它們,比如我們可以把常用的函數放在一個Utils.gradle腳本里,供其他腳本文件引用。示例中我們把 App的版本名稱和版本號單獨放在一個腳本文件裏,清晰、簡單、方便、快捷.我們也可以使用自動化對該文件自動處理,生成版本。

說說Gradle插件的作用

把插件應用到你的項目中,插件會擴展項目的功能,幫助你在項目的構建過程中做很多事情。1.可以添加任務到你的項目中,幫你完成一些亊情,比如測試、編譯、打包。2.可以添加依賴配置到你的項目中,我們可以通過它們配置我們項目在構建過程中需要的依賴.比 如我們編譯的時候依賴的第三方庫等。3.可以向項目中現有的對象類型添加新的擴展屬性、 方法等,讓你可以使用它們幫助我們配置、優化構建,比如android{}這個配置塊就是Android Gradle插件爲Project對象添加的一個擴展。4. 可以對項目進行一些約定,比如應用Java插 件之後,約定src/main/java目錄下是我們的源代碼存放位置,在編譯的時候也是編譯這個目錄下的Java源代碼文件。

然後我們說說’com.android.application’

Android Gradle插件的分類其實是根據Android工程的屬性分類的。在Andriod中有3類工程,一類是App應用工程,它可以生成一個可運行的apk應用:一類是Library庫工程,它可以生成AAR包給其他的App工程公用,就和我們的Jar一樣,但是它包含了Android的資源等信息,是一個特殊的Jar包;最後一類是Test測試工程,用於對App工程或者Library庫工程進行單元測試。

  • App插件id:com.android.application.
  • Library插件id:com.android.library.
  • Test插件id:com.android.test. 
    一般一個項目只會設置一個App插件,而module一般是會設置爲Library插件。 

android{}

是Android插件提供的一個擴展類型,可以讓我們自定義Android Gradle工程,是Android Gradle工程配置的唯一入口。

compileSdkVersion

是編譯所依賴的Android SDK的版本,這裏是API Level。

buildToolsVersion

是構建該Android工程所用構建工具的版本。

defaultConfig{}

defaultConfig是默認的配置,它是一個ProductFlavor。ProductFlavor允許我們根據不同的情況同時生成多個不同的apk包。

applicationId

配置我們的包名,包名是app的唯一標識,其實他跟AndroidManifest裏面的package是可以不同的,他們之間並沒有直接的關係。

package指的是代碼目錄下路徑;applicationId指的是app對外發布的唯一標識,會在簽名、申請第三方庫、發佈時候用到。

minSdkVersion

是支持的Android系統的api level,這裏是15,也就是說低於Android 15版本的機型不能使用這個app。

targetSdkVersion

表明我們是基於哪個Android版本開發的,這裏是22。

versionCode

表明我們的app應用內部版本號,一般用於控制app升級,當然我在使用的bugly自動升級能不能接受到升級推送就是基於這個。

versionName

表明我們的app應用的版本名稱,一般是發佈的時候寫在app上告訴用戶的,這樣當你修復了一個bug並更新了版本,別人卻發現說怎麼你這個bug還在,你這時候就可以自信的告訴他自己看下app的版本號。(親身經歷在撕逼的時候可以從容的應對)

multiDexEnabled

用於配置該BuildType是否啓用自動拆分多個Dex的功能。一般用程序中代碼太多,超過了65535個方法的時候。

ndk{}

多平臺編譯,生成有so包的時候使用,包括四個平臺’armeabi’, ‘x86’, ‘armeabi-v7a’, ‘mips’。一般使用第三方提供的SDK的時候,可能會附帶so庫。

sourceSets

源代碼集合,是Java插件用來描述和管理源代碼及資源的一個抽象概念,是一個Java源代碼文件和資源文件的集合,我們可以通過sourceSets更改源集的Java目錄或者資源目錄等。

譬如像上圖,我通過sourceSets告訴了Gradle我的關於jni so包的存放路徑就在app/libs上了,叫他編譯的時候自己去找。

name:build type的名字

applicationIdSuffix:應用id後綴

versionNameSuffix:版本名稱後綴

debuggable:是否生成一個debug的apk

minifyEnabled:是否混淆

proguardFiles:混淆文件

signingConfig:簽名配置

manifestPlaceholders:清單佔位符

shrinkResources:是否去除未利用的資源,默認false,表示不去除。

zipAlignEnable:是否使用zipalign工具壓縮。

multiDexEnabled:是否拆成多個Dex

multiDexKeepFile:指定文本文件編譯進主Dex文件中

multiDexKeepProguard:指定混淆文件編譯進主Dex文件中
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

buildType

構建類型,在Android Gradle工程中,它已經幫我們內置了debug和release兩個構建類型,兩種模式主要車別在於,能否在設備上調試以及簽名不一樣,其他代碼和文件資源都是一樣的。一般用在代碼混淆,而指定的混淆文件在下圖的目錄上,minifyEnabled=true就會開啓混淆: 
 

signingConfigs

簽名配置,一個app只有在簽名之後才能被髮布、安裝、使用,簽名是保護app的方式,標記該app的唯一性。如果app被惡意刪改,簽名就不一樣了,無法升級安裝,一定程度保護了我們的app。而signingConfigs就很方便爲我們提供這個簽名的配置。storeFile簽名文件,storePassword簽名證書文件的密碼,storeType簽名證書類型,keyAlias簽名證書中祕鑰別名,keyPassword簽名證書中改密鑰的密碼。

默認情況下,debug模式的簽名已經被配置好了,使用的就是Android SDK自動生成的debug證書,它一般位於$HOME/.android/debug.keystore,其key和密碼是已經知道的,一般情況下我們不需要單獨配置debug模式的簽名信息。 

productFlavors

在我看來他就是Gradle的多渠道打包,你可以在不同的包定義不同的變量,實現自己的定製化版本的需求。

manifestPlaceholders

佔位符,我們可以通過它動態配置AndroidManifest文件一些內容,譬如app的名字: 

看看上圖,我們就能發現我們在productFlavors中定義manifestPlaceholders = [APP_NAME: “(測試)”]之後,在AndroidManifest的label加上”${APP_NAME}”,我們就能控制每個包打出來的名字是我們想要不同的名字,譬如測試服務器和生產服務器的包應該名字不一樣。

buildConfigField

他是BuildConfig文件的一個函數,而BuildConfig這個類是Android Gradle構建腳本在編譯後生成的。而buildConfigField就是其中的自定義函數變量,看下圖我們分別定義了三個常量: 
 
我們可以在BuildConfig文件中看到我們聲明的三個變量 
 
然後我們就可以在代碼中用這些變量控制不同版本的代碼: 

我們這樣加個if,就可以輕輕鬆鬆的控制測試和生產版本付費的問題了,再也不用手動的改來改去了,那問題來了,我怎麼去選擇不同的版本呢,看下圖: 
 
如果你是Android Studio,找到Build Variants就可以選擇你當前要編譯的版本啦。

flavorDimensions

顧名思義就是維度,Gradle3.0以後要用flavorDimensions的變量必須在defaultConfig{}中定義才能使用,不然會報錯:

Error:All flavors must now belong to a named flavor dimension.
The flavor 'flavor_name' is not assigned to a flavor dimension.
  • 1
  • 2

這樣我們就可以在不同的包中形成不同的applicationId和versionName了。 

dexOptions{}

我們知道,Android中的Java源代碼被編譯成class字節碼後,在打包成apk的時候 
被dx命令優化成Android虛擬機可執行的DEX文件。DEX文件比較緊湊,Android費盡心思 
做了這個DEX格式,就是爲了能使我們的程序在Android中平臺上運行快一些。對於這些生成 
DEX文件的過程和處理,Android Gradle插件都幫我們處理好了,Android Gradle插件會調用 
SDK中的dx命令進行處理。但是有的時候可能會遇到提示內存不足的錯誤,大致提示異常是 
java,lang.OutOfMemoryError: GC overhead limit exceeded,爲什麼會提示內存不足呢? 其實這個 
dx命令只是一個腳本,它調用的還是Java編寫的dx.jar庫,是Java程序處理的,所以當內存 
不足的時候,我們會看到這個Java異常信息.默認情況下給dx分配的內存是一個G8,也就 
是 1024MB。

所以我們只需要把內存設置大一點,就可以解決這個問題,上圖我的項目就把內存設置爲4g。 

dependencies{}

我們平時用的最多的大概就這個了,

  • 首先第一句compile fileTree(include: [‘.jar’], dir: ‘libs’)*,這樣配置之後本地libs文件夾下的擴展名爲jar的都會被依賴,非常方便。
  • 如果你要引入某個本地module的話,那麼需要用compile project(‘×××’)。
  • 如果要引入網上倉庫裏面的依賴,我們需要這樣寫compile group:’com.squareup.okhttp3’,name:’okhttp’,version:’3.0.1’,當然這樣是最完整的版本,縮寫就把group、name、version去掉,然後以”:”分割即可。 
    compile ‘com.squareup.okhttp3:okhttp:3.0.1’ 
     
    但是到了gradle3.0以後build.gradle中的依賴默認爲implementation,而不是 
    之前的compile。另外,還有依賴指令api。 
    那麼下面我們就來說說:

gradle 3.0中依賴implementation、api的區別:

其實api跟以前的compile沒什麼區別,將compile全部改成api是不會錯的; 
而implementation指令依賴是不會傳遞的,也就是說當前引用的第三方庫僅限於本module內使用,其他module需要重新添加依賴才能用,下面用兩個圖說明:

 
 
相信看過圖的人都會一目明瞭。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章