上一章我们学习了Gralde的使用,创建和转换Android工程。本章我们将深入了解构建文件,学习一些有用的tasks,探索Gradle和Android插件。
本章我们关注以下内容:
- 理解Gradle文件
- 学习使用构建任务
- 自定义构建
理解Gradle文件
在Android Studio中创建新工程的时候,默认生成3个Gradle文件:
MyApp
├── build.gradle
├── settings.gradle
└── app
└── build.gradle
这三个文件各有用途,我们在以下面进行探索。
settings.gradle
新的工程只包含一个app
模块,settings.gradle
:
include ':app'
settings.gradle
文件在initialization期间执行,定义了构建过程需要包含的模块。上述代码表示需要包含app
模块。对于单模块的工程,settings.gradle
文件不是必需的。多模块的工程必须有该文件,否则Gradle不知道需要包含哪些模块。
Gradle会为每个settings.gradle
文件创建一个Settings
对象,并触发该对象相应的方法。
顶级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团队开发和维护,提供了用于构建、测试和打包Android应用和库的所有任务。
Android
android
代码块包含了整个Android特有的配置,由Android插件提供。必需的属性仅仅是compileSdkVersion
和buildToolsVersion
:
- 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构建系统之前,AndroidManifest.xml
中的包名有两个功能:
- 作为app的唯一标识
- 作为R.java文件的包名
Gradle利用构建变量使创建不同版本的app变得简单了许多。比如你可以非常简单的创建一个free版本和一个paid版本。这两个版本需要有不同的app标识,这样它们才能同时存在于Google应用商店,同时被安装到手机上。然而源代码和生成的R.java文件必须时刻保持同样的包名。否则你所有的源代码都需要改变。这就是Android Tools团队将包名这两个功能分离开的原因。manifest文件中的package
继续作为R.java文件的包名,而设备和Google Play所需的app标识则由applicationId替代。在我们体验build types之后,applicationId
将更加有趣。
defaultConfig
接下来的两个属性是minSdkVersion
和targetSdkVersion
。这两个属性应该比较熟悉了,它们定义在manifest文件的<uses-sdk>
元素中。minSdkVersion
用来配置运行app的最低的API。targetSdkVersion
告知测试app需要的特定Android版本,这样操作系统不需要启用任何正向兼容性行为。这与我们之前看到的compileSdkVersion
无关。
versionCode
和versionName
也与manifest中的一致。
构建文件的所有值都会替换manifest文件的值。所以如果你在build.gradle
文件定义了这些值,就没有必要在manifest文件再次定义。只有当构建文件没有定义这些值的时候,manifest中的值才会起作用。
buildTypes
块是你定义如何构建和打包不同app构建类型的地方。我们将在第四章详细学习构建类型。
dependencies
dependencies
块是Gradle标准配置的一部分(这就是它为什么放在了android
块的外部的原因),定义了app或者library所需的全部依赖。Android app默认将libs
文件夹作为全部JAR依赖文件的目录。取决于你在新工程向导中勾选的选项,它也可能依赖AppCompat
库。我们将在第三章讨论依赖。
开始学习任务
为了得知工程中有多少可用任务,你可以运行gradlew tasks
命令。在一个新创建的工程中,这些可用任务包含Android tasks,build tasks,build setup tasks,help tasks,install tasks,verification tasks和其他tasks。如果你还想看到任务间的依赖关系,可以运行gradlew tasks --all
命令。演示一个任务也是可以的,它可以打印出执行一项任务所需的所有步骤。演示不会真正去执行这些步骤,它是一种查看运行特定任务结果的安全方式。你可以通过添加参数-m
或者--dry-run
来演示任务。
基础任务
Gradle Android插件使用了Java基础插件,Java基础插件又使用了基础插件。这些添加了标准生命周期任务和一些通用属性。基础插件定义了assemble
和clean
任务,Java基础插件定义了check
和build
任务。这些任务并没有在基础插件中实现,不包含任何的actions;它们用来定义添加执行作业的实际tasks的规则。
这些任务的规则有:
- assemble收集工程的输出
- clean清理工程的输出
- check运行所有的检查,通常是单元测试和设备测试
-
build运行
assemble
和check
Java基础插件也添加了source sets的概念。Android插件建立在这些约定之上,从而暴露了那些有经验的Gradle用户习惯于看到的任务。在这些基础任务之上,Android插件也添加了许多Android特有的任务。
Android任务
Android插件扩展、实现了基础任务。下面是在Android环境中这些任务的功能:
- assemble为每一个build type 创建一个APK
- clean删除所有构建结果,包括APK文件
- check运行Lint检查,并在查到问题时停止构建
- build运行assemble和check
assemble
任务默认依赖于assembleDebug
和assembleRelease
,在你添加更多build type时,它会依赖于更多任务。这意味着运行assembe
将构建每一个build type。
除了扩展这些任务,Android插件也添加了一些任务。下面是最重要的一些任务:
- connectedCheck在连接的真机或者虚拟器上运行测试
- deviceCheck为其他插件在远程设备运行测试提供的接口
- installDebug、installRelease在设备或者虚拟器上安装特定版本
- uninstall每一个install任务有对应的uninstall任务
build
任务依赖于check
,但不依赖connectedCheck
和deviceCheck
。这是为了确保常规检查不要求有连接的设备或者运行的模拟器。运行check
任务会生成一个Lint报告,包含所有的warnings和errors,并有相应的解释和文档链接。这个报告在app/build/outputs
目录,名为lint-results.html
。
在你assemble release时,Lint会检查引起app崩溃的问题。在它发现问题时,它将停止构建,并在命令行中打印错误。Lint也会在app/build/outputs
目录生成一个名为lint-results-release-fatal.html
的报告。如果你有多个问题,浏览HTML报告比查看命令行更加方便。报告提供的链接也很有用,它们会指向问题的细节。
Inside Android Studio
使用gradle窗口查看任务。本节略过。
自定义构建
Android Studio的构建。
操作manifest文件的条目
我们前面已经见过在构建文件中配置applicationId, minSdkVersion,targetSdkVersion,versionCode和versionName
。你还可以操作一些其他的属性:
- testApplicationId设备测试APK的applicationId
- testInstrumentationRunner运行测试所用到的JUnit测试器的名称(见第六章,运行测试)
- signingConfig(见第四章,创建构建变体)
- proguardFile proguardFiles(见第九章,高级自定义构建)
Inside Android Studio
使用窗口配置工程,本节略过。
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_URL
和BuildConfig.LOG_HTTP
这两个值了。
最近,Android Tools团队还添加了配置resources资源的特性:
android {
buildTypes {
debug {
resValue "string", "app_name", "Example DEBUG"
}
release {
resValue "string", "app_name", "Example"
}
}
}
这里不需要为Example DEBUG
和Example
两个值添加\"
转义的双引号,因为资源值默认用value=""
包装。
工程范围的设置
如果你的工程中有多个模块,为所有模块添加统一配置而非手动去配置每一个模块会显得非常有用。我们已经见过如何在顶级构建文件中使用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
本例中我们创建了一个新任务。我们将在第七章详解任务语法
在命令行中运行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而不指定具体任务时,将会运行clean
和assembleDebug
任务。你也可以输入如下代码查看当前的默认任务:
$ gradlew tasks | grep "Default tasks"
Default tasks: clean, assembleDebug