随着Google对Eclipse的无情抛弃以及Studio的不断壮大,Android开发者逐渐拜倒在Studio的石榴裙下。 而作为Studio的默认编译方式,Gradle已逐渐普及。我最开始是被它的多渠道打包所吸引。
简介
Gradle是以Groovy语言为基础,面向Java应用为主。基于DSL(Domain Specific Language)语法的自动化构建工具。
Gradle集合了Ant的灵活性和强大功能,同时也集合了Maven的依赖管理和约定,从而创造了一个更有效的构建方式。凭借Groovy的DSL和创新打包方式,Gradle提供了一个可声明的方式,并在合理默认值的基础上描述所有类型的构建。 Gradle目前已被选作许多开源项目的构建系统。
因为Gradle是基于DSL语法的,如果想看到build.gradle文件中全部可以选项的配置,可以看这里
DSL Reference
基本的项目设置
一个Gradle项目通过一个在项目根目录中的build.gradle文件来描述它的构建。
Build文件最简单的Android应用中的build.gradle都会包含以下几个配置:
Project根目录的build.gradle:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}Module中的build.gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
...
}buildscript { ... }配置了编译时的代码驱动. 这种情况下,它声明所使用的是jCenter仓库。还有一个声明所依赖的在Maven文件的路径。这里声明的包含了Android插件所使用的1.5.0版本的Gradle. 注意:这只会影响build中运行的代码,不是项目中。项目中需要声明它自己所需要仓库和依赖关系。apply plugin : com.android.application,声明使用com.androdi.application插件。这是构建Android应用所需要的插件。android{...}配置了所有Android构建时的参数。默认情况下,只有编译的目标版本以及编译工具的版本是需要的。重要: 这里只能使用com.android.application插件。如果使用java插件将会报错。
module/src/main下的目录结构,因为有时候很多人把so放到libs目录就会报错:
如果项目的结构不标准的时候,可能就需要去配置它。Android插件使用了相似的语法,但是因为它有自己的sourceSets,所以要在android代码块中进行配置。下面就是一个从Eclipse的老项目结构中配置主要代码并且将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')
}
}就像有些人就是要把so放到libs目录中(这类人有点犟),那就需要这样进行修改。
注意:因为在旧的项目结构中所有的源文件(Java,AIDL和RenderScript)都放到同一个目录中,我们需要将sourceSet中的这些新部件都设置给src目录。
Build Tasks
对构建文件声明插件时通常或自动创建一些列的构建任务去执行。不管Java插件还是Android插件都是这样。Android常规的任务如下:
assemble生成项目output目录中的内容的任务。check执行所有的检查的任务。build执行assemble和check的任务。clean清理项目output目录的任务。在Android项目中至少会有两种output输出:一个debug apk和一个release apk。他们都有自己的主任务来分别执行构建:
assembleassembleDebugassembleRelease 提示:Gradle支持通过命令行执行任务首字母缩写的方式。例如:
在没有其他任务符合aR的前提下,gradle aR与gradle assembleRelease是相同的。
最后,构建插件创建了为所有build type(debug, release, test)类型安装和卸载的任务,只要他们能被安装(需要签名)。
installDebuginstallReleaseuninstallAlluninstallDebuguninstallReleaseuninstallDebugAndroidTestBuild定制Android插件提供了一些列的DSL来让直接从构建系统中做大部分的定制。
Manifest整体部分DSL提供了很多重要的配置manifest文件的参数,例如:
minSdkVersiontargetSdkVersionversionCodeversionNameapplicationIdtestApplicationIdtestInstrumentationRunnderAndroid Plugin DSL Reference提供了一个完整的构建参数列表。
把这些manifest属性放到build文件中的一个重要功能就是它可以被动态的设置。例如,可以通过读取一个文件或者其他逻辑来获取版本名称。
def computeVersionName() {
...
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
versionCode 12
versionName computeVersionName()
minSdkVersion 16
targetSdkVersion 23
}
}注意:不要使用可能与现有给定冲突的方法名。例如defaultConfig{...}中使用getVersionName()方法将会自动使用defaultConfig.getVersionName()来带起自定义的方法。
Build Types默认情况下Android插件会自动将应用程序设置成有一个debug版本和一个release版本。
这就是通过调用BuildType对象完成。默认情况下会创建两个实例,一个debug实例和一个release实例。Android插件同样允许通过其他的Build Types来定制其他的实例。这就是通过buildTypes来设置的:
android {
buildTypes {
debug {
applicationIdSuffix ".debug"
}
jnidebug {
initWith(buildTypes.debug)
applicationIdSuffix ".jnidebug"
jniDebuggable true
}
}
}上面的代码执行了以下操作:
debug的Build Type: applicationId。这样debug模式就能与release模式的apk同时安装在同一手机上。jnidebug的Build Type,并且把它设置为debug的拷贝。JNI组件的debug和增加一个新的包名后缀来继续定制该Build Type。 不管使用initWith()还是使用其他的代码块,创建一个新的Build Types都是非常简单的在buildTypes代码块中创建一个新的元素就可以了。
为应用签名需要使用如下几个部分:
A keystoreA keystore passwordA key alias nameA key passwordThe store type默认情况下有一个debug的配置,设置了一个debug的keystore,有一个已知的密码。debug keystore的位置是在$HOME/.android/debug.keystore,如果没有的话他会被默认创建。Debug的Build Type会默认使用该debug的签名设置。
当然也可以通过使用DSL语法中的signingconfigs部分来创建其他的配置来进行定制:
android {
signingConfigs {
debug {
storeFile file("debug.keystore")
}
myConfig {
storeFile file("other.keystore")
storePassword "android"
keyAlias "androiddebugkey"
keyPassword "android"
}
}
buildTypes {
foo {
signingConfig signingConfigs.myConfig
}
}
}上面的设置将把debug keystore的位置改为项目的根目录。同样也创建了一个新的签名配置,并且有一个新的Build Type使用它。
Dependencies, Android Libraries and Multi-project setupGradle项目可以依赖其他的外部二进制包、或者其他的Gradle项目。
想要配置依赖一个外部jar包,需要在compile的配置中添加一个dependency。下面的配置是添加了所有在libs目录的jar包:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
android {
...
}注意:DSL元素中的dependencies是Gradle API中的标准元素。不属于andorid元素。
compile配置是用来编译主应用的。它配置的所有部分都会被打包到apk中。当然也有一些其他的配置:
compile: main applicationandroidTestCompile:test applicationdebugCompile:debug Build Typerelease Compile:release Build Type当然我们可以使用compile和<buildtype>.compile这两种配置。创建一个新的Build Type通常会自动基于它的名字创建一个新的配置部分。这样在像debug版本而release版本不适用的一些特别的library时非常有用。
Gradle只是使用Maven和Ivy仓库。但是仓库必须要添加到列表中,并且必须声明所依赖仓库的Maven或者Ivy定义。
repositories {
jcenter()
}
dependencies {
compile 'com.google.guava:guava:18.0'
}
android {
...
}注意:jcenter()是指定仓库URL的快捷设置。Gradle支持远程和本地仓库。
注意:Gradle会直接识别所有的依赖关系。这就意味着如果一个依赖库自身又依赖别的库时,他们会被一起下下来。
AAR库dependencies {
compile(name:'本地aar库的名字,不用加后缀', ext:'aar')
}Gradle项目通常使用多项目设置来依赖其他的gradle项目。例如:
Gradle会通过下面的名字来引用他们:
:app
:libraries:lib1
:libraries:lib2
每个项目都会有一个单独的build文件,并且在项目的根目录还会有一个setting.gradle文件:
setting.gradle文件中的内容非常简单。它指定了哪个目录是Gralde项目:
include ':app', ':libraries:lib1', ':libraries:lib2':app这个项目可能会依赖其他的libraries,这样可以通过如下进行声明:
dependencies {
compile project(':libraries:lib1')
}Library项目上面用到了:libraries:lib1和:libraries:lib2可以是Java项目,:app项目会使用他们俩的输出的jar包。但是如果你需要使用android资源等,这些libraries就不能是普通的Java项目了,他们必须是Android Library项目。
Library项目Library项目和普通的Android项目的区别比较少,由于libraries的构建类型与应用程序的构建不同,所有它会使用一个别的构建插件。但是他们所使用的插件内部有很多相同的代码,他们都是由com.android.tools.build.gradle这个jar包提供的。
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
}Library项目的区别Library项目的主要输出我.aar包。它结合了代码(例如jar包或者本地.so文件)和资源(manifest,res,assets)。每个library也可以单独设置Build Type等来指定生成不同版本的aar。
Lint Support你可以通过指定对应的变量来设置lint的运行。可以通过添加lintOptions来进行配置:
android {
lintOptions {
// turn off checking the given issue id's
disable 'TypographyFractions','TypographyQuotes'
// turn on the given issue id's
enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
// check *only* the given issue id's
check 'NewApi', 'InlinedApi'
}
}Build变量构建系统的一个目标就是能对同一个应用创建多个不同的版本。
Product flavors一个product flavor可以针对一个项目制定不同的构建版本。一个应用可以有多个不同的falvors来改变生成的应用。
Product flavors是通过DSL语法中的productFlavors来声明的:
android {
....
productFlavors {
flavor1 {
...
}
flavor2 {
...
}
}
}Build Type + Product Flavor = Build Variant像我们之前看到的,每个Build Type都会生成一个apk.Product Flavors也是同样的:项目的输出僵尸所有Build Types与Product Flavors的结合。每种结合方式称之为Build Variant。例如,如果有debug和release版本的Build Types,上面的例子就会生成4种Build Variants:
Flavor1 - debugFlavor1 - releaseFlavor2 - debugFlavor2 - release没有配置flavors的项目仍然有Build Variants,它只是用了一个默认的flavor/config,没有名字,这导致variants的列表和Build Types的列表比较相同。
Product Flavor配置android {
...
defaultConfig {
minSdkVersion 8
versionCode 10
}
productFlavors {
flavor1 {
applicationId "com.example.flavor1"
versionCode 20
}
flavor2 {
applicationId "com.example.flavor2"
minSdkVersion 14
}
}
}注意android.productFlavors.*对象ProductFlavor有android.defaultConfig是相同的类型。这就意味着他们有相同的属性。
defaultConfig为所有的flavors提供了一些基本的配置,每个flavor都已重写他们。在上面的例子中,这些配置有:
flavor1 applicationId: com.example.flavor1 minSdkVersion: 8 versionCode: 20flavor2 applicationId: com.example.flavor2 minSdkVersion: 14 versionCode: 10 通常,Build Type配置会覆盖其他的配置。例如,Build Type的applicationIdSuffix会添加到Product Flavor的applicationId上。
最后,就像Build Types一样,Product Flavors也可以有他们自己的依赖关系。例如,如果有一个单独的flavors会使用一些广告或者支付,那这个flavors生成的apk就会使用广告的依赖,而其他的flavors就不需要使用。
dependencies {
flavor1Compile "..."
}BuildConfig在编译阶段,Android Studio会生成一个叫做BuildConfig的类,该类包含了编译时使用的一些变量的值。你可以观看这些值来改变不同变量的行为:
private void javaCode() {
if (BuildConfig.FLAVOR.equals("paidapp")) {
doIt();
else {
showOnlyInPaidAppDialog();
}
}下面是BuildConfig中包含的一些值:
boolean DEBUG - if the build is debuggable int VERSION_CODE String VERSION_NAMEString APPLICATION_IDString BUILD_TYPE- Build Type的名字,例如release String FLAVOR - flavor的名字,例如flavor1 ProGuard配置Android插件默认会使用ProGuard插件,并且如果Build Type中使用ProGuard的minifyEnabled属性开启的话,会默认创建对应的task。
android {
buildTypes {
release {
minifyEnabled true
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
productFlavors {
flavor1 {
}
flavor2 {
proguardFile 'some-other-rules.txt'
}
}
}Tasks控制基本的Java项目有一系列的tasks一起制作输出文件。
classes task就是编译Java源码的任务。 我们可以在build.gradle中通过使用classes很简单的获取到它。就是project.tasks.classes.
在Android项目中,更多的编译task,因为他们的名字通过Build Types和Product Flavors生成。
为了解决这个问题,android对象有两种属性:
applicationVariants - only for the app plugin android.applicationVariants.all { variant ->
....
}libraryVariants - only for the library plugin testVariants - for both pluginsundefined这些都会返回一个ApplicationVariant, LibraryVariant,TestVariant的DomainObjectCollection接口的实现类对象。undefinedDomainObjectCollection提供了直接获取或者很方便的间接获取所有对象的方法。 可以使用compileOptions代码块来设置编译时使用的语言版本。默认是基于compileSdkVersion的值。
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
}
}Resource ShrinkingGradle构建系统支持资源清理:对构建的应用会自动移除无用的资源。不仅会移除项目中未使用的资源,而且还会移除项目所以来的类库中的资源。注意,资源清理只能在与代码清理结合使用(例如ProGuad)。这就是为什么它能移除所依赖类库的无用资源。通常,类库中的所有资源都是使用的,只有类库中无用代码被移除后这些资源才会变成没有代码引用的无用资源。
android {
...
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。