2 基本项目配置(Basic Project Setup)

Gradle项目的构建描述文件为位于项目根目录下的build.gradle文件。(查看Gradle构建系统的用户指南)

2.1 简单的构建文件(Simple build files)

最简单的Android项目的build.gradle文件内容如下:

buildscript {

    repositories {
        jcenter()
    }

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

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.1.0"
}

该Android构建文件有三个主要部分:

buildscript { ... } 配置驱动构建的代码。该实例中声明使用JCenter中央仓库资源,并且声明依赖于该Maven工件(artifact)的一个类路径(classpath)。这个工件(artifact)是一个包含1.3.1版本Android插件的Gradle库。注意:该配置只作用于代码构建过程,并非应用于项目本身,项目需要声明属于自己的仓库(Repositories)依赖(Dependencies),这在稍后会提及。

接下来,应用com.android.application插件。该插件用于构建Android应用。

最后,android { ... } 配置了所有Android构建的参数。这是Android DSL的入口点。默认情况下,只有配置编译版本和编译工具版本,即compileSdkVersionbuildtoolsVersion属性。该编译版本相当于旧构建系统中project.properites的target属性,新属性可以与旧target属性一样配置int类型(API版本号)或者string类型的值。

重点:你只需要应用com.android.application插件,同时应用java插件会导致构建错误。

注意:你同样需要在相同路径添加local.properties文件,并使用sdk.dir属性设置本地SDK路径。另外,你也可以通过设置ANDROID_HOME环境变量。这两种方式没有什么不同,你可以选择一个你喜欢的方式。local.properties文件示例:

sdk.dir=/path/to/Android/Sdk

2.2 项目结构(Project Structure)

上面提到的基本构建文件需要一个默认的文件夹结构。Gradle遵循约定优于配置的概念,尽可能地提供合理的默认配置参数。基本的项目存在两个“源集合(source sets)”的组件,即主要源代码和测试代码。它们分别位于:

  • src/main/
  • src/androidTest/

在这些目录下存在着每个源组件对应的子目录。对于Java及Android插件,Java源码和Java资源文件路径为:

  • java/
  • resources/

对于Android插件,存在特定的额外文件和文件夹:

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

这意味着主要源码集(source sets)的.java文件位于src/main/java目录,主要清单(manifest)文件为src/main/AndroidManifest.xml

注意:*src/androidTest/AndroidManifest.xml*是不需要的,它会自动被创建。

2.2.1 配置项目结构(Configuring the Structure)

当默认的项目结构不适当时,就需要进行手动配置。查看Gradle文档,了解在纯java项目中这是如何做到的。

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

注意:因为旧的项目结构将所有的源文件(Java,AIDL及RenderScript)放置在相同目录,所以我们需要重新映射源集合(sourceSet)中所有组件到src目录。

注意:setRoot()将移动整个源集合(sourceSet)及其子目录到新目录下。示例中将src/androidTest/*目录移动到tests/*目录。这是Android的特殊配置,在Java源集合(sourceSets)中无效。

2.3 构建任务(Build Tasks)

2.3.1 一般任务(General Tasks)

应用一个插件到构建文件将自动创建一系列构建任务去运行,Java插件及Android插件都是如此。任务的约定如下:

  • assemble 该任务组合项目的运行输出
  • check 该任务运行所有检查
  • build 该任务运行assemble任务和check任务
  • clean 该任务清空项目的运行输出

assemble任务,check任务和build任务并未做确切的工作,仅仅只是作为“锚”任务,而插件则依赖它们来做一些确切的工作。

这就允许你总是调用相同的任务,而不必考虑是什么类型的项目,或者应用了什么插件。例如,应用findbugs插件将会提交一个新的任务,同时使check任务依赖于此任务,使得每当check任务被调用时此任务同样被调用到。

在命令行中,你可以执行以下命令获取到高级别的任务:

gradle tasks

获取所有任务列表和它们之间的依赖关系,可以运行以下命令:

gradle tasks --all

注意:Gradle自动监视一个任务声明的输入输出。

运行build任务两次,并且期间未做任何修改,Gradle报告所有任务为UP-TO-DATE状态,这表明任务不需要运行。这使得任务可以通过正确的依赖关系,从而避免不必要的构建操作。

2.3.2 Java项目任务(Java project tasks)

这是由Java插件创建的最重要的两个任务,依赖于主要的锚任务:

  • assemble
    • jar 该任务创建输出
  • check
    • test 该任务运行测试

jar任务直接或间接地依赖于其他任务:例如classes用于编译Java代码。测试则是由testClasses来编译的,但是作为test的依赖很少使用(类似classes)。

通常情况下,你可能只需要调用assemblecheck任务,不需要调用其他任务。你可以查看Gradle文档获取Java插件的所有任务及其描述。

2.3.3 Android任务(Android tasks)

 Android插件使用同样的约定来保证与其他插件的兼容性,同时也添加了额外的锚任务:

  • assemble 该任务组装项目的运行输出
  • check 该任务运行所有检查
  • connectedCheck 运行检查需要一个已连接设备或虚拟机,将会同时运行在所有已连接设备
  • deviceCheck 使用API连接到远程设备运行检查,用于CI服务器
  • build 该任务运行assemble任务及check任务
  • clean 该任务清空项目的运行输出

新的锚任务是必须的,主要用于保证无设备连接时能够运行定期检查。注意build未依赖于deviceCheckconnectedCheck

一个Android项目至少有两个输出:debug版本APK和release版本APK。每个输出也都有各自的锚任务来帮助构建:

  • assemble
    • assembleDebug
    • assembleRelease

它们都依赖于其他任务来完成构建APK所需要运行的多个步骤。其中assemble任务依赖于这两个任务,所以调用assemble时会导致构建两个APK。

提示:Gradle命令行支持任务名称的骆驼格式缩写。例如:

gradle aR

等同于执行以下命令:

gradle assembleRelease

只要没有名称与'aR'重复的任务即可

check锚任务有自己的依赖关系:

  • check
    • lint
  • connectedCheck
    • connectedAndroidTest
  • deviceCheck
    • 该任务依赖于其他插件实现测试扩展的任务

最后,所有能被安装(需要签名)的构建类型(debugreleasetest),插件会为其创建安装任务和卸载任务,如:

  • installDebug
  • installRelease
  • uninstallAll
    • uninstallDebug
    • uninstallRelease
    • uninstallDebugAndroidTest

2.4 基本构建定制(Basic Build Customization)

Android插件提供一个广泛的DSL用于直接从构建系统定制大部分功能。

2.4.1 清单条目(Manifest entries)

通过DSL可以配置最重要的清单(manifest)条目,例如:

  • minSdkVersion
  • targetSdkVersion
  • versionCode
  • versionName
  • applicationId(有效的packageName,了解ApplicationId与PackageName)
  • testApplicationId(用于test APK)
  • testInstrumentationRunner

示例:

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

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

请查看Android插件DSL参考来获取完整的可配置构建属性及默认值。

将清单(manifest)属性放在构建文件中的好处在于可以动态地调整这些值。例如,可以通过读取文件或者使用其他自定义的逻辑来获取版本名称:

def computeVersionName() {
    ...
}

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

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

注意:不要使用在该范围内会导致冲突的getter方法名称。例如在defaultConfig { ...}里调用getVersionName()方法,将自动调用defaultConfig.getVersionName()而不是自定义方法。

2.4.2 构建类型(Build Types)

默认情况下,Android插件自动设置项目构建一个应用程序的debug和release版本。它们最大的不同在于是否能够在一个安全设备(非开发版)上调试应用以及APK如何签名。debug版本是通过一个已知的用户名/密码自动创建的key/certificate进行签名(在构建中签名以防止未签名提示)。release版本在构建中并未签名,需要稍后进行签名。

这些配置通过一个BuildType对象来配置。默认情况下,创建了debug和release两个实例。Android插件允许定制这两个实例,并且创建其他的构建类型(Build Type)。可以在buildTypesDSL容器中配置:

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

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

以上代码实现以下功能:

  • 配置默认的debug构建类型(Build Type),设置包名为\.debug,使得debug和release版本的apk能安装在同一个设备上。
  • 创建名为jnidebug的构建类型(Build Type),并设置为debug构建类型(Build Type)的副本。
  • 继续配置jnidebug,开启JNI组件的调试模式,并添加一个不同的包名后缀。

创建一个新构建类型(Build Type)与在buildTypes下使用新元素一样容易,可以调用initWith()或者使用闭包配置。查看DSL参考获取在构建类型(Build Type)中所有可配置属性。

除了用来修改构建属性,构建类型(Build Type)也可以用来添加特定代码或资源。对于每个构建类型(Build Type),都会创建一个相对应的源集合(SourceSet),默认的位置为src/\/。例如src/debug/java目录可以被用来添加只在调试APK中使用的资源。这意味着构建类型(Build Type)的名称不能是mainandroidTest(这是由插件执行的),同时也必须保证唯一。

与其他的源码集合(source sets)一样,构建类型(Build Type)的源目录可以重定向:

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

另外,对于每个编译类型(Build Type),都会创建一个新的assemble\任务,如assembleDebug。我们已经提及过的assembleDebugassembleRelease,这里讲诉了它们是如何创建的。当debugrelease构建类型(Build Type)被创建前,它们对应的任务已经自动创建了。根据同样的规则,以上在build.gradle中的代码片段同样也生成了assembleJnidebug任务,并且assemble任务会像依赖于assembleDebug任务和assembleRelease任务一样,也依赖于assembleJnidebug任务。

提示:记住,你可以输入gradle aJ来运行assembleJnidebug任务。

可能使用的情况:

  • 只在调试模式下权限
  • 在调试模式下的自定义实现
  • 调试模式下不同的资源(例如与签名证书相关的资源)

构建类型(Build Type)下的代码/资源,遵循以下使用规则:

  • 清单(manifest)文件合并到app的清单(manifest)文件
  • 代码作为另外一个源代码目录
  • 资源覆盖主资源,替换已存在的资源

2.4.3 签名配置(Signing Configurations)

一个应用签名需要以下信息(查看签名你的应用了解关于签名APK的详情):

  • keystore
  • keystore密码
  • key alias名称
  • key alias密码
  • 存储类型

keystore位置、key alias名称与密码,及存储类型组成了一个签名配置。默认情况下,debug配置使用了一个已知的密码和已知的key作为调试的keystore。调试的keystore位于$HOME/.android/debug.keystore,如果不存在的话会自动创建。debug构建类型(Build Type)自动设置使用调试SigningConfig

允许创建其他配置或者自定义默认配置,只需通过signingConfigs DSL容器:

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

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

    buildTypes {
        foo {
            signingConfig signingConfigs.myConfig
        }
    }
}

以上代码片段更改调试keystore的位置到项目的根目录。这会自动影响到使用该配置的任何构建类型(Build Type),比如debug构建类型(Build Type)。同时也创建一个新的签名配置和一个使用该配置的新构建类型(Build Type)。

注意:只有位于默认位置的调试keystore会自动创建,变更后的路径不会自动创建调试keystore。如果创建一个使用默认调试keystore的签名配置,会使之自动创建调试keystore。换句话说,这与keystore的位置相关,而不是配置名称。

注意:keystore的位置通常相对于项目根目录,也可以设置绝对路径,但并不推荐。

注意:如果你提交这些文件到版本控制中,你可能不希望在文件中配置密码,Overflow提供了从控制台或者环境变量读取密码的方式。