Gradle for Android(二) 基本的構建定制

上一章我們學習了Gralde的使用,創(chuàng)建和轉換Android工程。本章我們將深入了解構建文件,學習一些有用的tasks,探索Gradle和Android插件。

本章我們關注以下內容:

  • 理解Gradle文件
  • 學習使用構建任務
  • 自定義構建

理解Gradle文件

在Android Studio中創(chuàng)建新工程的時候,默認生成3個Gradle文件:

MyApp
├── build.gradle
├── settings.gradle
└── app
    └── build.gradle

這三個文件各有用途,我們在以下面進行探索。

settings.gradle

新的工程只包含一個app模塊,settings.gradle

include ':app'

settings.gradle文件在initialization期間執(zhí)行,定義了構建過程需要包含的模塊。上述代碼表示需要包含app模塊。對于單模塊的工程,settings.gradle文件不是必需的。多模塊的工程必須有該文件,否則Gradle不知道需要包含哪些模塊。

Gradle會為每個settings.gradle文件創(chuàng)建一個Settings對象,并觸發(fā)該對象相應的方法。

頂級build.gradle

在頂級build.gradle中,你可以對工程中所有的模塊進行配置,它默認包含兩個代碼塊:

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

allprojects {
    repositories {
        jcenter()
    }
}

buildscript用來配置當前的構建。

repositories配置JCenter作為倉庫。我們在app或者library中可以使用JCenter這個依賴源提供的可供下載的各種庫。JCenter是一個廣泛使用的Maven倉庫。

dependencies代碼塊用來配置構建過程需要的依賴。這意味著你不應該在頂級配置文件中包含你的applications或者libraries需要的依賴。Gradle Android插件是僅有的默認包含的插件。這對每個Android模塊都是必須的,因為該插件是運行Android相關任務的必要插件。

allprojects代碼塊可以用來定義需要應用到所有模塊中的屬性。你還可以在該代碼塊中定義任務。這些任務可以用于所有的模塊。

一旦你使用了allprojects,modules就和project耦合到一起了。這意味著你不能脫離project的build.gradle文件單獨構建每個模塊。起初看起來不是問題,但隨后你可能會把內部的library分離到單獨的project中,這時你就需要重構構建文件了。

模塊的build.gradle

模塊級別的build.gradle文件包含應用于Android app模塊的配置。它也可以覆寫來自頂級build.gradle文件的配置。模塊級別的build.gradle文件是這個樣子的:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "com.gradleforandroid.gettingstarted"
        minSdkVersion 14
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.0'
}

下面我們來分析3個主要的塊

Plugin

第一行引入了Android應用插件,它在頂級構建文件中被配置為一個依賴。Android插件由Google的Android Tools團隊開發(fā)和維護,提供了用于構建、測試和打包Android應用和庫的所有任務。

Android

android代碼塊包含了整個Android特有的配置,由Android插件提供。必需的屬性僅僅是compileSdkVersionbuildToolsVersion:

  • compileSdkVersion是Android的API版本
  • buildToolsVersion是build tools的版本

build tools包含命令行工具,比如aapt,zipalign,dx和renderscript。你可以通過SDK Manager來下載build tools。

defaultConfig代碼塊配置了app的核心屬性。該塊中的屬性覆蓋了AndroidManifest.xml文件中相應的屬性:

defaultConfig {
    applicationId "com.gradleforandroid.gettingstarted"
    minSdkVersion 14
    targetSdkVersion 22
    versionCode 1
    versionName "1.0"
}

applicationId覆寫了manifest文件中的package(包名),但是兩者又有些不同。在Gradle作為默認的Android構建系統(tǒng)之前,AndroidManifest.xml中的包名有兩個功能:

  • 作為app的唯一標識
  • 作為R.java文件的包名

Gradle利用構建變量使創(chuàng)建不同版本的app變得簡單了許多。比如你可以非常簡單的創(chuàng)建一個free版本和一個paid版本。這兩個版本需要有不同的app標識,這樣它們才能同時存在于Google應用商店,同時被安裝到手機上。然而源代碼和生成的R.java文件必須時刻保持同樣的包名。否則你所有的源代碼都需要改變。這就是Android Tools團隊將包名這兩個功能分離開的原因。manifest文件中的package繼續(xù)作為R.java文件的包名,而設備和Google Play所需的app標識則由applicationId替代。在我們體驗build types之后,applicationId將更加有趣。

defaultConfig接下來的兩個屬性是minSdkVersiontargetSdkVersion。這兩個屬性應該比較熟悉了,它們定義在manifest文件的<uses-sdk>元素中。minSdkVersion用來配置運行app的最低的API。targetSdkVersion告知測試app需要的特定Android版本,這樣操作系統(tǒng)不需要啟用任何正向兼容性行為。這與我們之前看到的compileSdkVersion無關。

versionCodeversionName也與manifest中的一致。

構建文件的所有值都會替換manifest文件的值。所以如果你在build.gradle文件定義了這些值,就沒有必要在manifest文件再次定義。只有當構建文件沒有定義這些值的時候,manifest中的值才會起作用。

buildTypes塊是你定義如何構建和打包不同app構建類型的地方。我們將在第四章詳細學習構建類型。

dependencies

dependencies塊是Gradle標準配置的一部分(這就是它為什么放在了android塊的外部的原因),定義了app或者library所需的全部依賴。Android app默認將libs文件夾作為全部JAR依賴文件的目錄。取決于你在新工程向導中勾選的選項,它也可能依賴AppCompat庫。我們將在第三章討論依賴。

開始學習任務

為了得知工程中有多少可用任務,你可以運行gradlew tasks命令。在一個新創(chuàng)建的工程中,這些可用任務包含Android tasks,build tasks,build setup tasks,help tasks,install tasks,verification tasks和其他tasks。如果你還想看到任務間的依賴關系,可以運行gradlew tasks --all命令。演示一個任務也是可以的,它可以打印出執(zhí)行一項任務所需的所有步驟。演示不會真正去執(zhí)行這些步驟,它是一種查看運行特定任務結果的安全方式。你可以通過添加參數-m或者--dry-run來演示任務。

基礎任務

Gradle Android插件使用了Java基礎插件,Java基礎插件又使用了基礎插件。這些添加了標準生命周期任務和一些通用屬性。基礎插件定義了assembleclean任務,Java基礎插件定義了checkbuild任務。這些任務并沒有在基礎插件中實現,不包含任何的actions;它們用來定義添加執(zhí)行作業(yè)的實際tasks的規(guī)則。
這些任務的規(guī)則有:

  • assemble收集工程的輸出
  • clean清理工程的輸出
  • check運行所有的檢查,通常是單元測試和設備測試
  • build運行assemblecheck

Java基礎插件也添加了source sets的概念。Android插件建立在這些約定之上,從而暴露了那些有經驗的Gradle用戶習慣于看到的任務。在這些基礎任務之上,Android插件也添加了許多Android特有的任務。

Android任務

Android插件擴展、實現了基礎任務。下面是在Android環(huán)境中這些任務的功能:

  • assemble為每一個build type 創(chuàng)建一個APK
  • clean刪除所有構建結果,包括APK文件
  • check運行Lint檢查,并在查到問題時停止構建
  • build運行assemble和check

assemble任務默認依賴于assembleDebugassembleRelease,在你添加更多build type時,它會依賴于更多任務。這意味著運行assembe將構建每一個build type。

除了擴展這些任務,Android插件也添加了一些任務。下面是最重要的一些任務:

  • connectedCheck在連接的真機或者虛擬器上運行測試
  • deviceCheck為其他插件在遠程設備運行測試提供的接口
  • installDebug、installRelease在設備或者虛擬器上安裝特定版本
  • uninstall每一個install任務有對應的uninstall任務

build任務依賴于check,但不依賴connectedCheckdeviceCheck。這是為了確保常規(guī)檢查不要求有連接的設備或者運行的模擬器。運行check任務會生成一個Lint報告,包含所有的warnings和errors,并有相應的解釋和文檔鏈接。這個報告在app/build/outputs目錄,名為lint-results.html

在你assemble release時,Lint會檢查引起app崩潰的問題。在它發(fā)現問題時,它將停止構建,并在命令行中打印錯誤。Lint也會在app/build/outputs目錄生成一個名為lint-results-release-fatal.html的報告。如果你有多個問題,瀏覽HTML報告比查看命令行更加方便。報告提供的鏈接也很有用,它們會指向問題的細節(jié)。

Inside Android Studio

使用gradle窗口查看任務。本節(jié)略過。

自定義構建

Android Studio的構建。

操作manifest文件的條目

我們前面已經見過在構建文件中配置applicationId, minSdkVersion,targetSdkVersion,versionCode和versionName。你還可以操作一些其他的屬性:

  • testApplicationId設備測試APK的applicationId
  • testInstrumentationRunner運行測試所用到的JUnit測試器的名稱(見第六章,運行測試)
  • signingConfig(見第四章,創(chuàng)建構建變體)
  • proguardFile proguardFiles(見第九章,高級自定義構建)

Inside Android Studio

使用窗口配置工程,本節(jié)略過。

BuildConfig和resources

自SDK tools R17以來,build tools便會生成一個BuildConfig類,它包含了根據build type設置的DEBUG常量。當你只想在debug下運行一些代碼時,這將會很有用,比如打印日志等。Gradle允許你擴展該文件,這樣你就可以在debug和release版本擁有不同的常量了。

這些常量在開關某些特性或者設置服務器地址時,非常有用,比如:

android {
    buildTypes {
        debug {
            buildConfigField "String", "API_URL","\"http://test.example.com/api\""
            buildConfigField "boolean", "LOG_HTTP_CALLS", "true"}
        release {
            buildConfigField "String", "API_URL","\"http://example.com/api\""
            buildConfigField "boolean", "LOG_HTTP_CALLS", "false"
        }
    }
}

\"中的轉義字符是必需的,這樣才能生成實際的字符串。添加了buildConfigField之后,你就可以在Java代碼中使用BuildConfig.API_URLBuildConfig.LOG_HTTP這兩個值了。

最近,Android Tools團隊還添加了配置resources資源的特性:

android {
    buildTypes {
        debug {
            resValue "string", "app_name", "Example DEBUG"
        }
        release {
            resValue "string", "app_name", "Example"
        }
    }
}

這里不需要為Example DEBUGExample兩個值添加\"轉義的雙引號,因為資源值默認用value=""包裝。

工程范圍的設置

如果你的工程中有多個模塊,為所有模塊添加統(tǒng)一配置而非手動去配置每一個模塊會顯得非常有用。我們已經見過如何在頂級構建文件中使用allprojects塊來定義倉庫,你可以用同樣的策略來應用Android特有的設置:

allprojects {
    apply plugin: 'com.android.application'
    android {
        compileSdkVersion 22
        buildToolsVersion "22.0.1"
    }
}

這只在你的所有模塊都是Android工程的時候才會起作用,因為你需要應用Android插件來進行Android特有的設置。一個更好的方法是在頂級構建文件中定義相應的值,然后在每個模塊中引用這些值。Gradle可以在Project對象中添加額外的屬性。這意味著每個build.gradle文件都可以定義額外的屬性,這些屬性在ext塊中。

你可以在頂級構建文件中使用ext塊:

ext {
    compileSdkVersion = 22
    buildToolsVersion = "22.0.1"
}

可以在模塊級構建文件中通過rootProject調用這些屬性:

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
}

工程屬性

上例中的ext塊是定義額外屬性的一種方式。你可以利用屬性自定義構建過程,我們將在第七章講述如何利用它們來自定義任務。有幾種方式可以定義屬性,這里我們著重介紹如下三種:

  • ext
  • gradle.properties文件
  • -P命令行參數

下面這個build.gradle文件包含了這三種方式:

ext {
    local = 'Hello from build.gradle'
}

task printProperties << {
    println local // Local extra property
    println propertiesFile // Property from file
    if (project.hasProperty('cmd')) {
        println cmd // Command line property
    }
}

下面是相應的gradle.properties文件:

propertiesFile = Hello from gradle.properties

本例中我們創(chuàng)建了一個新任務。我們將在第七章詳解任務語法

在命令行中運行printProperties任務,得到如下結果:

$ gradlew printProperties -P cmd='Hello from the command line'
:printProperties
Hello from build.gradle
Hello from gradle.properties
Hello from the command line

有了自定義屬性,修改構建配置就和修改單個屬性一樣簡單。

在頂級構建文件和模塊級構建文件中定義屬性都是可以的。模塊級構建文件中的同名屬性會覆蓋頂級構建文件的屬性

默認任務

如果你只是運行Gradle,并不明確指定某個任務,它將會打印一些如何使用Gradle的信息。這是因為help任務是Gradle的默認任務。你也可以重寫默認任務,指定一個或者多個任務為默認任務,這樣就可以只運行Gradle而不指定具體任務了。

為了指定默認任務,你需要在頂級構建文件中添加如下代碼:

defaultTasks 'clean', 'assembleDebug'

現在當你運行Gradle Wrapper而不指定具體任務時,將會運行cleanassembleDebug任務。你也可以輸入如下代碼查看當前的默認任務:

$ gradlew tasks | grep "Default tasks"
Default tasks: clean, assembleDebug
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容