鍍金池/ 教程/ Android/ 基本項目 - Basic Project
依賴關(guān)系,Android 庫和多項目設(shè)置 - Dependencies,Android Libraries and Multi-
要求 - Requirements
構(gòu)建變種版本 - Build Variants
高級構(gòu)建定制 - Advanced Build Customization
測試 - Testing
基本項目 - Basic Project
簡介 - Introduction

基本項目 - Basic Project

一個Gradle項目的構(gòu)建過程定義在build.gradle文件中,位于項目的根目錄下。

簡單的構(gòu)建文件

一個最簡單的Gradle純Java項目的build.gradle文件包含以下內(nèi)容:

    apply plugin: 'java'

這里引入了Gradle的Java插件。這個插件提供了所有構(gòu)建和測試Java應(yīng)用程序所需要的東西。

最簡單的Android項目的build.gradle文件包含以下內(nèi)容:

    buildscript {
        repositories {
            mavenCentral()
        }

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

    apply plugin: 'android'

    android {
        compileSdkVersion 19
        buildToolsVersion "19.0.0"
    }

這里包括了Android build file的3個主要部分:

buildscrip{...}這里配置了驅(qū)動構(gòu)建過程的代碼。

在這個部分,它聲明了使用Maven倉庫,并且聲明了一個maven文件的依賴路徑。這個文件就是包含了0.11.1版本android gradle插件的庫。

注意:這里的配置只影響控制構(gòu)建過程的代碼,不影響項目源代碼。項目本身需要聲明自己的倉庫和依賴關(guān)系,稍后將會提到這部分。

接下來,跟前面提到的Java Plugin一樣添加了android plugin。

最后,andorid{...}配置了所有android構(gòu)建過程需要的參數(shù)。這里也是Android DSL的入口點。

默認(rèn)情況下,只需要配置目標(biāo)編譯SDK版本和編譯工具版本,即compileSdkVersionbuildToolsVersion屬性。 這個complieSdkVersion屬性相當(dāng)于舊構(gòu)建系統(tǒng)中project.properites文件中的target屬性。這個新的屬性可以跟舊的target屬性一樣指定一個int或者String類型的值。

重要:
你只能添加android plugin。同時添加java plugin會導(dǎo)致構(gòu)建錯誤。

注意:
你同樣需要在相同路徑下添加一個local.properties文件,并使用sdk.dir屬性來設(shè)置SDK路徑。 另外,你也可以通過設(shè)置ANDROID_HOME環(huán)境變量,這兩種方式?jīng)]有什么不同,根據(jù)你自己的喜好選擇其中一種設(shè)置。

項目結(jié)構(gòu)

上面提到的基本的構(gòu)建文件需要一個默認(rèn)的文件夾結(jié)構(gòu)。Gradle遵循約定優(yōu)先于配置的概念,在可能的情況盡可能提供合理的默認(rèn)配置參數(shù)。

基本的項目開始于兩個名為“source sets”的組件,即main source code和test code。它們分別位于:

  • src/main/
  • src/androidTest/

里面每一個存在的文件夾對應(yīng)相應(yīng)的源組件。 對于Java plugin和Android plugin來說,它們的Java源代碼和資源文件路徑如下:

  • java/
  • resources/

但對于Android plugin來說,它還擁有以下特有的文件和文件夾結(jié)構(gòu):

  • AndroidManifest.xml
  • res/
  • assets/
  • aidl/
  • rs/
  • jni/

注意:
src/androidTest/AndroidManifest.xml是不需要的,它會自動被創(chuàng)建。

配置結(jié)構(gòu)

當(dāng)默認(rèn)的項目結(jié)構(gòu)不適用的時候,你可能需要去配置它。根據(jù)Gradle文檔,重新為Java項目配置_sourceSets_可以使用以下方法:

    sourceSets {
        main {
            java {
                srcDir 'src/java'
            }
            resources {
                srcDir 'src/resources'
            }
        }
    }

注意:
srcDir將會被添加到指定的已存在的源文件夾中(這在Gradle文檔中沒有提到,但是實際上確實會這樣執(zhí)行)。

替換默認(rèn)的源代碼文件夾,你可能想要使用能夠傳入一個路徑數(shù)組的srcDirs來替換單一的srcDir。以下是使用調(diào)用對象的另一種不同方法:

    sourceSets {
        main.java.srcDirs = ['src/java']
        main.resources.srcDirs = ['src/resources']
    }

想要獲取更多信息,可以參考Gradle文檔中關(guān)于Java Pluign的部分。

Android Plugin使用的是類似的語法。但是由于它使用的是自己的sourceSets,這些配置將會被添加在android對象中。

以下是一個示例,它使用了舊項目結(jié)構(gòu)中的main源碼,并且將androidTest _sourceSet_組件重新映射到_tests_文件夾。

    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']
            }

            androidTest.setRoot('tests')
        }
    }

注意:
由于舊的項目結(jié)構(gòu)將所有的源文件(java,aidl,renderscripthe和java資源文件)都放在同一個目錄里面,所以我們需要將這些_sourceSet_組件重新映射到src目錄下。

注意:
setRoot()方法將移動整個組件(包括它的子文件夾)到一個新的文件夾。示例中將會移動src/androidTest/*tests/*下。
以上這些是Android特有的,如果配置在Java的_sourceSets_里面將不會有作用。

以上也是將舊構(gòu)建系統(tǒng)項目遷移到新構(gòu)建系統(tǒng)需要做的遷移工作。

構(gòu)建任務(wù)

通用任務(wù)

添加一個插件到構(gòu)建文件中將會自動創(chuàng)建一系列構(gòu)建任務(wù)(build tasks)去執(zhí)行(注:gradle屬于任務(wù)驅(qū)動型構(gòu)建工具,它的構(gòu)建過程是基于Task的)。Java plugin和Android plugin都會創(chuàng)建以下task:

  • assemble
    這個task將會組合項目的所有輸出。

  • check
    這個task將會執(zhí)行所有檢查。

  • build
    這個task將會執(zhí)行assemble和check兩個task的所有工作

  • clean
    這個task將會清空項目的輸出。

實際上assemble,check,build這三個task不做任何事情。它們只是一個Task標(biāo)志,用來告訴android plugin添加實際需要執(zhí)行的task去完成這些工作。

這就允許你去調(diào)用相同的task,而不需要考慮當(dāng)前是什么類型的項目,或者當(dāng)前項目添加了什么plugin。 例如,添加了findbugs plugin將會創(chuàng)建一個新的task并且讓check task依賴于這個新的task。當(dāng)check task被調(diào)用的時候,這個新的task將會先被調(diào)用。

在命令行環(huán)境中,你可以執(zhí)行以下命令來獲取更多高級別的task:

    gradle tasks

查看所有task列表和它們之間的依賴關(guān)系可以執(zhí)行以下命令:

    gradle tasks --all

注意:
Gradle會自動監(jiān)視一個task聲明的所有輸入和輸出。
兩次執(zhí)行build task并且期間項目沒有任何改動,gradle將會使用UP-TO-DATE通知所有task。這意味著第二次build執(zhí)行的時候不會請求任何task執(zhí)行。這允許task之間互相依賴,而不會導(dǎo)致不需要的構(gòu)建請求被執(zhí)行。

Java 項目的 Task

Java plugin主要創(chuàng)建了兩個task,依賴于main task(一個標(biāo)識性的task):

  • assemble
    • jar
      這個task創(chuàng)建所有輸出
  • check
    • test
      這個task執(zhí)行所有的測試。

jar task自身直接或者間接依賴于其他task:classes task將會被調(diào)用于編譯java源碼。
testClasses task用于編譯測試,但是它很少被調(diào)用,因為test task依賴于它(類似于classes task)。

通常情況下,你只需要調(diào)用到assemblecheck,不需要其他task。

你可以在Gradle文檔中查看java plugin的全部task。

Android 任務(wù)

Android plugin使用相同的約定以兼容其他插件,并且附加了自己的標(biāo)識性task,包括:

  • assemble
    這個task用于組合項目中的所有輸出。
  • check
    這個task用于執(zhí)行所有檢查。
  • connectedCheck
    這個task將會在一個指定的設(shè)備或者模擬器上執(zhí)行檢查,它們可以同時在所有連接的設(shè)備上執(zhí)行。
  • deviceCheck
    通過APIs連接遠(yuǎn)程設(shè)備來執(zhí)行檢查,這是在CL服務(wù)器上使用的。
  • build
    這個task執(zhí)行assemble和check的所有工作。
  • clean
    這個task清空項目的所有輸出。

這些新的標(biāo)識性task是必須的,以保證能夠在沒有設(shè)備連接的情況下執(zhí)行定期檢查。 注意build task不依賴于deviceCheck或者connectedCheck

一個Android項目至少擁有兩個輸出:debug APK(調(diào)試版APK)和release APK(發(fā)布版APK)。每一個輸出都擁有自己的標(biāo)識性task以便能夠單獨構(gòu)建它們。

  • assemble
    • assembleDebug
    • assembleRelease

它們都依賴于其它一些tasks以完成構(gòu)建一個APK需要多個步驟。其中assemble task依賴于這兩個task,所以執(zhí)行assemble將會同時構(gòu)建出兩個APK。

小提示:
gradle在命令行終端上支持駱駝命名法的task簡稱,例如,執(zhí)行
gradle aR
命令等同于執(zhí)行
gradle assembleRelease

check task也擁有自己的依賴:

  • check
    • lint
  • connectedCheck
    • connectedAndroidTest
    • connectedUiAutomatorTest(目前還沒有應(yīng)用到)
  • deviceCheck
    • 這個test依賴于test創(chuàng)建時,其它實現(xiàn)測試擴(kuò)展點的插件。

最后,只要task能夠被安裝(那些要求簽名的task),android plugin就會為所有構(gòu)建類型(debug,releasetest)安裝或者卸載。

基本的構(gòu)建定制

Android plugin提供了大量DSL用于直接從構(gòu)建系統(tǒng)定制大部分事情。

Manifest 屬性

通過SDL可以配置一下manifest選項:

  • minSdkVersion
  • targetSdkVersion
  • versionName
  • applicationId (有效的包名 -- 更多詳情請查閱ApplicationId 對比 PackageName)
  • package Name for the test application
  • Instrumentation test runner

例如:

    android {
        compileSdkVersion 19
        buildToolsVersion "19.0.0"

        defaultConfig {
            versionCode 12
            versionName "2.0"
            minSdkVersion 16
            targetSdkVersion 16
        }
    }

android元素中的defaultConfig元素中定義所有配置。

之前的Android Plugin版本使用packageName來配置manifest文件中的packageName屬性。從0.11.0版本開始,你需要在build.gradle文件中使用applicationId來配置manifest文件中的packageName屬性。 這是為了消除應(yīng)用程序的packageName(也是程序的ID)和java包名所引起的混亂。

在構(gòu)建文件中定義的強(qiáng)大之處在于它是動態(tài)的。 例如,可以從一個文件中或者其它自定義的邏輯代碼中讀取版本信息:

    def computeVersionName() {
        ...
    }

    android {
        compileSdkVersion 19
        buildToolsVersion "19.0.0"

        defaultConfig {
            versionCode 12
            versionName computeVersionName()
            minSdkVersion 16
            targetSdkVersion 16
        }
    }

注意:
不要使用與在給定范圍內(nèi)的getter方法可能引起沖突的方法名。例如,在defaultConfig{...}中調(diào)用getVersionName()將會自動調(diào)用defaultConfig.getVersionName()方法,你自定義的getVersionName()方法就被取代掉了。

如果一個屬性沒有使用DSL進(jìn)行設(shè)置,一些默認(rèn)的屬性值將會被使用。以下表格是可能使用到的值:

Property Name Default value in DSL object Default value
versionCode -1 value from manifest if present
versionName null value from manifest if present
minSdkVersion -1 value from manifest if present
targetSdkVersion -1 value from manifest if present
applicationId null value from manifest if present
testApplicationId null applicationId + “.test”
testInstrumentationRunner null android.test.InstrumentationTestRunner
signingConfig null null
proguardFile N/A (set only) N/A (set only)
proguardFiles N/A (set only) N/A (set only)

如果你在構(gòu)建腳本中使用自定義代碼邏輯請求這些屬性,那么第二列的值將非常重要。例如,你可能會寫:

    if (android.defaultConfig.testInstrumentationRunner == null) {
        // assign a better default...
    }

如果這個值一直保持null,那么在構(gòu)建執(zhí)行期間將會實際替換成第三列的默認(rèn)值。但是在DSL元素中并沒有包含這個默認(rèn)值,所以,你無法查詢到這個值。
除非是真的需要,這是為了預(yù)防解析應(yīng)用的manifest文件。

構(gòu)建類型

默認(rèn)情況下,Android Plugin會自動給項目設(shè)置同時構(gòu)建應(yīng)用程序的debug和release版本。 兩個版本之間的不同主要圍繞著能否在一個安全設(shè)備上調(diào)試,以及APK如何簽名。

Debug版本采用使用通用的name/password鍵值對自動創(chuàng)建的數(shù)字證書進(jìn)行簽名,以防止構(gòu)建過程中出現(xiàn)請求信息。Release版本在構(gòu)建過程中沒有簽名,需要稍后再簽名。

這些配置通過一個BuildType對象來配置。默認(rèn)情況下,這兩個實例都會被創(chuàng)建,分別是一個debug版本和一個release版本。

Android plugin允許像創(chuàng)建其他構(gòu)建類型一樣定制debugrelease實例。這需要在buildTypes的DSL容器中配置:

    android {
        buildTypes {
            debug {
                applicationIdSuffix ".debug"
            }

            jnidebug.initWith(buildTypes.debug)
            jnidebug {
                packageNameSuffix ".jnidebug"
                jnidebugBuild true
            }
        }
    }

以上代碼片段實現(xiàn)了以下功能:

  • 配置默認(rèn)的debug構(gòu)建類型
    • 將debug版本的包名設(shè)置為.debug以便能夠同時在一臺設(shè)備上安裝_debug_和_release_版本的apk。
  • 創(chuàng)建了一個名為jnidebug的新構(gòu)建類型,并且這個構(gòu)建類型是debug構(gòu)建類型的一個副本。
  • 繼續(xù)配置jnidebug構(gòu)建類型,允許使用JNI組件,并且也添加了不一樣的包名后綴。

創(chuàng)建一個新的構(gòu)建類型就是簡單的在buildType標(biāo)簽下添加一個新的元素,并且可以使用initWith()或者直接使用閉包來配置它。

以下是一些可能使用到的屬性和默認(rèn)值:

Property name Default values for debug Default values for release / other
debuggable true false
jniDebugBuild false false
renderscriptDebugBuild false false
renderscriptOptimLevel 3 3
applicationIdSuffix null null
versionNameSuffix null null
signingConfig android.signingConfigs.debug null
zipAlign false true
runProguard false false
proguardFile N/A (set only) N/A (set only)
proguardFiles N/A (set only) N/A (set only)

除了以上屬性之外,_Build Type_還會受項目源碼和資源影響: 對于每一個Build Type都會自動創(chuàng)建一個匹配的sourceSet。默認(rèn)的路徑為:

    src/<buildtypename>/

這意味著_BuildType_名稱不能是_main_或者androidTest(因為這兩個是由plugin強(qiáng)制實現(xiàn)的),并且他們互相之間都必須是唯一的。

跟其他sourceSet設(shè)置一樣,Build Type的source set路徑可以重新被定向:

    android {
        sourceSets.jnidebug.setRoot('foo/jnidebug')
    }

另外,每一個_Build Type_都會創(chuàng)建一個新的assemble任務(wù)。

assembleDebugassembleRelease兩個Task在上面已經(jīng)提到過,這里要講這兩個Task從哪里被創(chuàng)建。當(dāng)debugrelease構(gòu)建類型被預(yù)創(chuàng)建的時候,它們的tasks就會自動創(chuàng)建對應(yīng)的這個兩個Task。

上面提到的build.gradle代碼片段中也會實現(xiàn)assembleJnidebug task,并且assemble會像依賴于assembleDebugassembleRelease一樣依賴于assembleJnidebug。

提示:你可以在終端下輸入gradle aJ去運行assembleJnidebug task

可能會使用到的情況:

  • release模式不需要,只有debug模式下才使用到的權(quán)限
  • 自定義的debug實現(xiàn)
  • 為debug模式使用不同的資源(例如當(dāng)資源的值由綁定的證書決定)

_BuildType_的代碼和資源通過以下方式被使用:

  • manifest將被混合進(jìn)app的manifest
  • 代碼行為只是另一個資源文件夾
  • 資源將疊加到main的資源中,并替換已存在的資源。

簽名配置

對一個應(yīng)用程序簽名需要以下:

  • 一個Keystory
  • 一個keystory密碼
  • 一個key的別名
  • 一個key的密碼
  • 存儲類型

位置,鍵名,兩個密碼,還有存儲類型一起形成了簽名配置。

默認(rèn)情況下,debug被配置成使用一個debug keystory。 debug keystory使用了默認(rèn)的密碼和默認(rèn)key及默認(rèn)的key密碼。 debug keystory的位置在$HOME/.android/debug.keystroe,如果對應(yīng)位置不存在這個文件將會自動創(chuàng)建一個。

debug Build Type(構(gòu)建類型) 會自動使用debug SigningConfig (簽名配置)。

可以創(chuàng)建其他配置或者自定義內(nèi)建的默認(rèn)配置。通過signingConfigs這個DSL容器來配置:

    android {
        signingConfigs {
            debug {
                storeFile file("debug.keystore")
            }

            myConfig {
                storeFile file("other.keystore")
                storePassword "android"
                keyAlias "androiddebugkey"
                keyPassword "android"
            }
        }

        buildTypes {
            foo {
                debuggable true
                jniDebugBuild true
                signingConfig signingConfigs.myConfig
            }
        }
    }

以上代碼片段修改debug keystory的路徑到項目的根目錄下。在這個例子中,這將自動影響其他使用到debug構(gòu)建類型的構(gòu)建類型。

這里也創(chuàng)建了一個新的Single Config(簽名配置)和一個使用這個新簽名配置的新的Build Type(構(gòu)建類型)。

注意:只有默認(rèn)路徑下的debug keystory不存在時會被自動創(chuàng)建。更改debug keystory的路徑并不會自動在新路徑下創(chuàng)建debug keystory。如果創(chuàng)建一個新的不同名字的SignConfig,但是使用默認(rèn)的debug keystore路徑來創(chuàng)建一個非默認(rèn)的名字的SigningConing,那么還是會在默認(rèn)路徑下創(chuàng)建debug keystory。換句話說,會不會自動創(chuàng)建是根據(jù)keystory的路徑來判斷,而不是配置的名稱。

注意:雖然經(jīng)常使用項目根目錄的相對路徑作為keystore的路徑,但是也可以使用絕對路徑,盡管這并不推薦(除了自動創(chuàng)建出來的debug keystore)。

注意:如果你將這些文件添加到版本控制工具中,你可能不希望將密碼直接寫到這些文件中。下面Stack Overflow鏈接提供從控制臺或者環(huán)境變量中獲取密碼的方法:
http://stackoverflow.com/questions/18328730/how-to-create-a-release-signed-apk-file-using-gradle
我們以后還會在這個指南中添加更多的詳細(xì)信息。

運行 Proguard

從Gradle Plugin for ProGuard version 4.10之后就開始支持ProGuard。ProGuard插件是自動添加進(jìn)來的。如果_Build Type_的_runProguard_屬性被設(shè)置為true,對應(yīng)的task將會自動創(chuàng)建。

    android {
        buildTypes {
            release {
                runProguard true
                proguardFile getDefaultProguardFile('proguard-android.txt')
            }
        }

        productFlavors {
            flavor1 {
            }
            flavor2 {
                proguardFile 'some-other-rules.txt'
            }
        }
    }

發(fā)布版本將會使用它的Build Type中聲明的規(guī)則文件,product flavor(定制的產(chǎn)品版本)將會使用對應(yīng)flavor中聲明的規(guī)則文件。

這里有兩個默認(rèn)的規(guī)則文件:

  • proguard-android.txt
  • proguard-android-optimize.txt

這兩個文件都在SDK的路徑下。使用_getDefaultProguardFile()_可以獲取這些文件的完整路徑。它們除了是否要進(jìn)行優(yōu)化之外,其它都是相同的。

精簡資源

你能夠在編譯的時候自動化地移除不用的資源。更多的信息,請看文檔 Resource Shrinking