Android Gradle 學習使用

第一部分   —  Android Gradle語法

目前,大多數講解 Gradle的文章都是先從複雜的 Gradle語法開始.而實際上, 對於 Android人員,掌握這些語法細節並沒有卵用,我們僅需要能看懂隨用隨查即可.那本文也是遵照實用這個原則介紹 Android Gradle.相信,讀過本文, 你至少應該不在畏懼 Build Script.

如果你對 build.gradle已經很熟悉,那麼直接參考 gooogle官方的 Android Plugin DSL Reference 即可.

下面進入主題.先來看下 As幫我們生成的有關於 Gradle的幾個文件().


如上圖,標準的 As項目中, 包含三大部分:

·        Top-level Gradle:用於配置所有的 Module屬性

·        Moudle-level Gradle:配置獨立 Moudle的屬性

·        Gradle Wrapper:用於統一編譯環境,一般供 CI 使用

下面來具體看看.

Top-level Build Script



有幾個概念,都來自於 Gradle Reference:

gradle script 都是 configuration scripts.這話的意思是說,運行起來後, 每個腳本文件最終都會對應到一個程序中的對象,這個對象叫做 delegate object.比如, build.gradle對應爲程序中的 Project.整個 Gradle 有三種類型的代理對象,分別是:

Type of script

Delegates to instance of

Build script

Project

Init script

Gradle

Settings script

Settings

通過上面的知識我們可以知道,在任何 build.gradle中都有一個內置的變量project.




我們可以通過 gradle -qproperties 查看腳本中所有的內建屬性.

下面,我們一步一步來看看這個 Top-level Build Script:

buildscript

 

buildscript {

    repositories {

        jcenter()

    }

    dependencies {

        classpath 'com.android.tools.build:gradle:2.2.0'

 

        // NOTE: Do not place your application dependencies here; they belong

        // in the individual module build.gradle files

    }

}

buildscript { }

Configures thebuild script classpath for this project.

The givenclosure is executed against this project’s ScriptHandler.The ScriptHandler ispassed to the closure as the closure’s delegate.

Delegates to:
ScriptHandler from buildscript

簡單來說buildscript 就是配置構建腳本所需要用到的 class path. 其內部會把 configuration closure傳遞給 ScriptHandler (你不用關心這是什麼)然後實現設置 repositories dependencies.

一般而言,這個 script block都寫在開頭,聲明這個腳本本身所需要的依賴.

allprojects

 

allprojects {

    repositories {

        jcenter()

    }

}

allprojects { }

Configures thisproject and each of its sub-projects.

This methodexecutes the given closure against this project and its sub-projects. Thetarget Project is passed tothe closure as the closure’s delegate.

Delegates to:
Each Project in allprojects

這個 configurationclosure裏的內容會被包括當前的 project以及其所有 sub-project執行.你在這裏配置的 repositories會在上述地方都生效.

這裏的寫法意味着,所有的 project都會在 jcenter()中尋找依賴.除此之外, 還可以指定 flatDirmavenivy .可以參考 RepositoryHandler.

task clean


task clean(type: Delete) {

    delete rootProject.buildDir

}

這是定義了一個新的 task,叫做 clean.其類型是 Delete.實際上, Android Plugin內置了 clean方法, 該方法位於 module. Module中內置的 clean 方法只會清理 Module 中的文件並刪除 Module 中的 build 目錄,但是工程根目錄下的 build文件是沒人清理的,所以這裏定義的 clean方法即刪除項目目錄下的 build文件夾.

Module-level Build Script Build Variant


模塊級 script用於描述該模塊的編譯過程.一般而言, Android能用到的一共有三種:

·        Application:對應 com.android.application.編譯的結果是一個 apk

·        Android Library:對應 com.android.library.編譯結果爲 aar

·        JavaLibrary:對應的 java.編譯結果爲 jar

你是不是好奇 android一共有多少種插件?可以看看 google android plugin 的倉庫

java 這個插件一般用不到,就不提了com.android.library com.android.application 內容配置差不太多,這裏以com.android.application 爲例講解.

由於 Module-levelBuild Script大部分都是相應插件自行實現的內容,所以我們就不能再 gradle文檔中找了,我們要到 google系的 refs 中搜索 ( refs).

你是不是好奇這個文件到底有多少個屬性?可以看看這個文件的源碼

ANDROID

android {

    compileSdkVersion 24

    ....

}

其實 application 插件支持的屬性遠遠比這個要多,咱們來看一個全集:

apply plugin: 'com.android.application'

 

android {

    /**

     * 設置編譯 sdk 和編譯工具的版本

     */

    compileSdkVersion 24

    buildToolsVersion "24.0.3"

 

    /**

     * 關於簽名, 請參考 google 官方文檔: <a href="https://developer.android.com/studio/publish/app-signing.html#debug-mode">Sign Your App</a>

     */

    signingConfigs {

        /**

         * As 會自動幫我們使用 debug certificate 進行簽名. 這個 debug certificate 每次安裝 As 都會變,

         * 因此不適合作爲發佈之用.

         */

        debug {

        }

 

        /**

         * 由於 Module-level Build Script(本文件) 也要放在 VCS 中管理, 所以不將密碼等信息寫在這裏.

         * 一般的做法是: 在本機設置環境變量, 然後通過下面代碼中演示的這種方式讀取.

         * 當然, 最佳實踐也指導我們將 `gradle.properties` <strong><em>排除</em>在 VCS 之外</strong>,

         * 此時, 也在該文件中將密碼設置爲變量, 然後在此讀取使用.

         */

        release {

            storeFile file("$System.env.STORE_FILE")

            storePassword "$System.env.STORE_PASSWORD"

            keyAlias "$System.env.KEY_ALIAS"

            keyPassword "$System.env.KEY_PASSWORD"

        }

    }

 

    /**

     * 爲所有的 build variants 設置默認的值. 關於 build variant, 我們後面會用一張圖片說明

     */

    defaultConfig {

        applicationId "com.walfud.myapplication"

        minSdkVersion 23

        targetSdkVersion 24

        versionCode 1

        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

 

    /**

     * type 默認會有 debug 和 release. 不管你寫不寫都如此.

     * 通常, 我們在 debug 中保留默認值, release 中開啓混淆, 並使用私有的簽名

     */

    buildTypes {

        debug {

            // 使用默認值

        }

 

        release {

            // 混淆

            minifyEnabled false

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

 

            // 簽名

            signingConfig signingConfigs.release

        }

    }

 

    /**

     * flavor 強調的是不同的版本, 比如付費版和免費版.

     * 在國內, 這個字段更多被用於區分不同的渠道, 即 360 渠道, 小米渠道等等.

     */

    productFlavors {

        m360 {}

        xiaomi {}

    }

 

    /**

     * 這個選項基本不用.

     * <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">官方說</a>: 使用 splits 可以比使用 flavor 更加有效創建多 apk.

     * 目前而言, 僅支持 Density 和 ABIs 這兩個分類.

     */

    splits {

        // 按屏幕尺寸

        density {

            enable true

 

            // 默認包含全部分辨率, 這裏是剔除一些我們不要的

            exclude "ldpi", "mdpi", "xxxhdpi", "400dpi", "560dpi", "tvdpi"

        }

 

        // 按架構

        abi {

            enable true

 

            // 使用 `reset()` 後, 我們就相當於不包含任何架構,

            // 這種情況下我們就可以通過 `include` 指定想要使用的架構

            reset()

 

            include 'x86', 'armeabi-v7a'

            universalApk true       // 是否同時生成一個包含全部 Architecture 的包

        }

    }

}

 

/**

 * 這個項目的依賴

 */

dependencies {

    /**

     * `fileTree` 導入 libs 目錄下的所有 jar 文件

     */

    compile fileTree(dir: 'libs', include: ['*.jar'])

 

    /**

     * 想導入本地 aar, 首先需要指明本地 aar 的位置, 如下 `repositories` 中所示, 我們把 aar 放在了

     * Module-level 的 libs 目錄下. 然後引用這個文件即可.

     */

    compile(name: 'components', ext: 'aar')

}

 

/**

 * 配置了去哪裏查找這個模塊依賴文件

 */

repositories {

    flatDir {

        dirs 'libs'

    }

}

Build Variant




簡單的說,就是爲了不同的渠道或者版本自動生成相應的 apk.其算法是 buildType * productFlavor * density * abi 做一個笛卡爾積.

至於優先級,參考 google 關於Build Variants的文檔.

第二部分   —  As裏的 Gradle

1. 自動下載

每一次重新導入工程時,因爲默認會使用 Use default gradle wrapper (recommended) 設置,所以 As 會下載 gradle-wrapper.properties distributionUrl 所指定的 gradle 版本.這個過程在國內普遍巨慢無比.我的建議是找到上述文件,動手修改 distributionUrl 爲已下載過的 gradle版本(比如你之前已經下載過 gradle-2.14.1),這樣就能直接打開了.

實際上,這個配置項位於 As自動生成的配置文件(.idea/gradle.xml),我們來看看:

對於新建 (‘File ->New -> New Project’)的項目,  As是這樣給我們生成的:



它在設置中的表現是這樣的:



可見, As幫我限定了 gradle版本.當然, 如果這個指定的 gradle版本不存在的話,一樣會去下載.

再來看看被 ‘File ->Open’ (注意,不是 Reopen)打開的項目:




也就是說,每次你 Open一個項目的時候, As會忽略你之前設置的 gradle版本, 而使用 wrapper文件中所指定的版本.所以你 clone 下來的代碼第一次打開都會卡很久的原因,就是因爲他們在下載 wrapper中的 gradle

2. gradle-wrapper.properties

AS 創建的工程,在根目錄下有 gradlew.bat  gradlew.sh 文件.這兩個文件會讀取 gradle/wrapper/gradle-wrapper.properties 並使用其中指定的 gradle 版本進行編譯.一般而言這套機制用於 CI服務器中來保障每次編譯都在同樣的環境下.

 

#Wed Nov 11 21:08:45 CST 2015

distributionBase=GRADLE_USER_HOME

distributionPath=wrapper/dists

zipStoreBase=GRADLE_USER_HOME

zipStorePath=wrapper/dists

distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip

GRADLE_USER_HOME 默認值是你的 USER_HOME/.gradle (win下是 c:\Users\<username>\.gradle, linux下是 ~/.gradle ).

對於 gradlew 而言,如果上述路徑沒有找到可執行的 gradle文件, 則會使用distributionUrl 中所指定的 url下載後在執行.

3. gradle.properties

主要作用配置一些與當前機器 gradle編譯相關的屬性.這些屬性是每個編譯機器根據自己情況決定的(比如,分配多大內存),因此不適合放在 git.

3.1 property 分爲兩種

3.1.1. SYSTEM PROPERTY

類似於系統的環境變量.作爲 jvm啓動參數.

a) Set


# gradle.properties

systemProp.xxx=yyy

system property 都以 systemProp.xxx 爲模板.

只有 top-level gradle.property 才能設置 system property.

系統提供瞭如下幾個內置變量:

org.gradle.daemon
是否開啓 daemon.一般來說,本機編譯建議打開, CI上建議關閉.

org.gradle.java.home
指定 gradle 進程的 java home. 沒什麼卵用.

org.gradle.jvmargs
daemon 進程的 jvm參數.當你編譯報錯 OOM 的時候, 可以調整這個參數 (見後面的例子)

org.gradle.configureondemand
自行 google. 沒卵用

org.gradle.parallel
project 之間並行編譯

當然,我們也可以通過命令行中指定 -D 來設置 system property.

b) Get

1

2

// build.gradle

System.properties['xxx']

3.1.2. PROJECT PROPERTY

一般用作 project內部的變量,保存用戶名密碼之類的私有值.

a) Set


# gradle.properties

xxx=yyy

也可以通過命令行中指定 -P 來設置 project property

b) Get


// build.gradle

println xxx

3.2.設置代理

 

systemProp.https.proxyHost=www.proxyhost.org

systemProp.https.proxyPort=8080

systemProp.https.proxyUser=userid

systemProp.https.proxyPassword=password

systemProp.https.nonProxyHosts=*.nonproxyrepos.com|localhost

3.3. 優先級 (下面的優先級更高)

project 目錄下的 gradle.properties

$GRADLE_USER_HOME/gradle.properties

環境變量或者命令行中使用 -Dsystem.property -Pproject.property設置

常見的這是什麼玩意兒

 

transitive = true


compile('com.crashlytics.sdk.android:answers:1.3.10@aar') {

    transitive = true;

}

簡單地說,由於該語句使用 @aar notation,所以 gradle 只會下載這一個 aar文件, 而不會順帶着下載這個 aar所需要的依賴文件.所以需要 transitive 讓依賴能夠自動被下載.一般而言,去掉 @aar 以及 { transitive = true } 不會有任何問題.

 

原文地址:http://android.walfud.com/android-gradle-%E7%9C%8B%E8%BF%99%E4%B8%80%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86/


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