Gradle是一种高级构建系统, 同时也是一个允许通过插件创建自定义构建逻辑的构建工具. 以下是选择的原因:
Gradle使用项目根目录下的 build.gradle 文件描述构建过程. (参见 Gradle User Guide )
最简单的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" }
其中有3个主要部分:
buildscript {...}
配置了驱动构建的代码. 在本例中, 它内部生命了所使用的jCenter仓库, 和一个Maven artifact依赖的classpath. 该artifact是包含用于Android的1.3.1版本的Gradle插件的库. 注意:
这只会影响执行构建的代码, 而不会影响项目. 该项目需要声明自身的仓库和依赖. 后面会讲到.
然后, 应用了 com.android.application
. 这是构建Android应用的插件.
最后, android {...}
配置了android构建的所有参数. 这是Android DSL的入口. 默认情况下, 只需要编译目标和构建工具的版本. 这通过 compileSdkVersion
和 buildtoolsVersion
属性来完成. 编译目标与 project.properties
中的 target
属性一致. 该属性可以赋值为int(api级别)或与 target
相同的字符串.
重要:你应该只使用 com.android.application
插件. 使用 java
插件会导致构建错误.
注意:你还需要一个 local.properties
文件来设置SDK的路径, 使用 sdk.dir
属性.
或者你可以设置一个环境变量, 命名为 ANDROID_HOME
.
以上两种方法没有本质区别, 你可以使用任意一种. local.properties 示例文件:
sdk.dir=/path/to/Android/Sdk
上面的基本构建文件用于默认的目录结构. Gradle遵循约定由于配置理念, 在可能的情况下会提供默认选项值. 基本项目会以两个叫做”source sets”的组件开始, 一个用于主要的源代码, 另一个用于测试代码. 目录如下:
src/main/
src/androidTest/
每个目录内部还包含子目录. 对于java和Android插件, Java源代码和资源在如下目录:
java/
resources/
对于Android插件, 会有如下额外的文件:
AndroidManifest.xml
res/
assets/
aidl/
rs/
jni/
jniLibs/
*.java
文件都位于 src/main/java
, 手册文件位于 src/main/AndroidManifest.xml
注意: src/androidTest/AndroidManifest.xml
会自动生成
当默认项目结构不足以使用时, 可以对其进行配置. 参见 这篇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/*
移动到了 test/*
. 这是Android的标准, 不能用于Java sourceSets.
在构建文件中配置一个插件可以自动生成一系列的构建任务. 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
的, 意味着不需要执行任何任务.
以下是 java
插件两个最重要的任务:
assemble
jar
check
test
jar
任务本身直接或间接的依赖于其他任务: 例如, classes
会编译java代码. 该测试使用 testClasses
进行编译, 但该命令没有什么用处, 因为 test
依赖于它(和 classes
)
总的来说, 你可能只需要调用 assemble
或 check
, 并忽略其他任务. 你可以在 这里
看到完整的Java插件任务描述
Android插件使用相同的约定来保持同其他插件的兼容, 并增加了额外的锚点任务:
assemble
check
connectedCheck
deviceCheck
build
assemble
和 check
clean
为了在不需要连接设备的情况下执行常规检查, 新的锚点任务是必要的. 注意 build
不依赖于 deviceCheck
或 connectedCheck
一个Android项目至少有两种输出: 一个debug APK和一个release APK. 每种输出都有自己的锚点任务来分别执行构建:
assemble
assembleDebug
assembleRelease
他们都依赖于其他任务. assemble
任务同时依赖于这两个任务, 因此运行该指令会构建两种APK.
提示:Gradle支持驼峰式的任务名称简写, 例如:
gradle aR
与下面的命令相同
gradle assembleRelease
只要没有其他任务匹配’aR’即可
check锚点任务有自己的依赖:
check
lint
connectedCheck
connectedAndroidTest
deviceCheck
最后, 插件会创建任务来安装和卸载所有构建类型(debug, release, test), 例如
installDebug
installRelease
uninstallAll
uninstallDebug
uninstallRelease
uninstallDebugAndroidTest
Android插件提供了一个宽泛的DSL来在构建系统中直接自定义大部分的内容.
通过DSL可以配置最重要的Manifest入口, 例如:
minSdkVersion
targetSdkVersion
versionCode
versionName
applicationId
(有效的包名, 参见 ApplicationId和PackageName
) testApplicationId
(用于测试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()
来替换自定义的方法.
默认情况下, Android插件会自动为项目配置debug和release版本. 两者的区别主要在于是否可以在安全的(非工程机)设备上调试应用, 以及APK的签名细节. debug版本使用自动生成的已知的名称/密码(避免在构建过程中输入密码)来进行签名. release版本在构建过程中不进行签名, 签名需要放在构建之后.
配置通过 BuildType
对象来完成. 默认情况下, 会创建两个实例, 一个 debug
一个 release
. Android插件允许自定义这两个实例, 或者创建其他的 Build Type
. 可以通过以下的 buildTypes
DSL容器实现:
android { buildTypes { debug { applicationIdSuffix ".debug" } jnidebug { initWith(buildTypes.debug) applicationIdSuffix ".jnidebug" jniDebuggable true } } }
上面的代码实现了如下功能:
debug
构建类型: <app applicationId>.debug
以便在同一个设备上同时安装debug和release的apk jnidebug
并使用了 debug
构建模式 jnidebug
, 启用了JNI组件的debug, 并添加了一个不同的包名前缀.
创建一个新的 BuildType
和在 buildTypes
容器中创建一个新元素一样简单. 可以调用 initWith()
或用括号来配置. 参见 DSL参考手册
来了解可用于配置构建类型的所有属性.
如果需要修改构建属性, BuildType
可以添加某些代码或资源. 对于每种构建类型, 都会创建一个与之匹配的 sourceSet
, 默认路径在 src/<buildtypename>/
, 例如 src/debug/java
目录只能添加debug APK所用的资源. 这意味着 BuildType
名称不能使用 main
或 androidTest
(这是插件强制设置的), 并且名称必须唯一.
和其他资源目录一样, 构建类型资源目录页可以重新定义:
android { sourceSets.jnidebug.setRoot('foo/jnidebug') }
此外, 对于每种 BuildType
, 都会创建一个新的 assemble<BuildTypeName>
任务, 例如 assembleDebug
. 这就是之前提到的 assembleDebug
和 assembleRelease
的来源. 当 debug
和 release
构建类型已经创建的情况下, 他们的任务也会自动被创建. 根据这一规则, 上面的 build.gradle
规则会生成一个 assembleJnidebug
任务, 并且 assemble
会依赖于该任务.
提示:记住你可以输入 gradle aJ
来运行 assembleJnidebug
任务
可能的用例:
BuildType 的代码/资源可以通过如下方式使用:
对一个应用签名有以下要求 (参见 签名应用 ):
路径, key名称, 密码和存储类型构成了一个 签名配置
. 默认情况下, debug
配置使用debug keystore, 它使用的是已知的密码和默认的key.
debug keystore位于 $HOME/.android/debug.keystore
, 如果没有的话会自动创建. debug
构建类型默认使用 debug
的 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 } } }
上面的代码将debug keystore的路径修改为项目的根目录. 这会自动影响所有使用该配置的的 Build Type
, 在本例中就是 debug
构建类型. 该代码同时会创建一个新的 Signing Config
, 并且新的构建类型会使用这个配置.
注意:只有默认路径下的debug keystore会被自动创建. 修改debug keystore路径不会生效. 使用其他名称在默认debug keystore路径创建 SigningConfig 可以被自动创建. 换句话说, 是否生成与keystore的路径有关, 而与配置的名称无关.
注意:keystore的路径通常相对于项目的根目录, 但也可以使用绝对路径, 虽然这种方式是不推荐的(除了debug的, 因为会被自动创建)
注意:如果你通过版本控制检出这些文件, 你可能不希望文件中出现密码. 这篇Stack Overflow文章 展示了如果从命令行, 或环境变量中读取值.
Gradle项目可以依赖于其他组件. 这些组件可以是外部二进制包, 或者其他Gradle项目.
配置依赖于外部库的jar, 你需要在 compile
配置中添加依赖. 以下代码在 libs
目录下添加了对所有jar的依赖:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) } android { ... }
注意: dependencies
DSL元素是标准Gradle API的一部分, 并且不属于 android
元素之内
compile
配置用于编译主应用. 编译配置中的所有内容都会被加入编译的classpath 和
最终的APK. 还有其他可能的配置可用于添加依赖:
compile
: 主应用 androidTestCompile
: 测试应用 debugCompile
: debug构建类型 releaseCompile
: release构建类型
由于不使用 Build Type
构建APK时不可能的, 所以APK通常会配置两种或多种配置: compile
和 <buildtype>Compile
. 创建一个新的 Build Type
会自动根据他的名字创建一个新的配置. 这在debug版本使用一个自定义库(比如报告崩溃)而release版本不需要使用该库的情况下, 或是他们依赖于不同版本的库的情况下很有用. (参见 Gradle文档
)
Gradle支持从Maven或Ivy仓库拉取artifact. 首先必须将仓库添加到列表中, 然后必须按照Maven或Ivy方式声明依赖.
repositories { jcenter() } dependencies { compile 'com.google.guava:guava:18.0' } android { ... }
注意: jcenter()
是指定仓库URL的快捷方式. Gradle支持远程和本地仓库.
注意:Gradle会跟进所有依赖. 也就是说如果一个依赖有他自己的其他依赖, 这些依赖也会被拉取.
更多配置依赖的信息请参见 Gradle用户指导 和 DSL文档
Gradle项目可以使用多项目设置同时依赖于其他Gradle项目. 多项目配置可以通过将所有项目作为制定根项目的子目录来实现. 例如, 有如下结构:
MyProject/ + app/ + libraries/ + lib1/ + lib2/
我们可以看到有3个项目. Gradle可以通过以下名称来引用他们:
:app :libraries:lib1 :libraries:lib2
每个项目都有有自己的 build.gradle 来声明如何进行构建. 此外, 在项目根目录还会有一个叫做 settings.gradle 的文件来声明项目. 结构如下:
MyProject/ | settings.gradle + app/ | build.gradle + libraries/ + lib1/ | build.gradle + lib2/ | build.gradle
settings.gradle的内容很简单. 它定义了哪个目录是一个Gradle项目:
include ':app', ':libraries:lib1', ':libraries:lib2'
:app
项目可能会依赖某些库, 可以通过如下的声明实现:
dependencies { compile project(':libraries:lib1') }
更多多项目设置的信息可参见 这里 .
在上面的多项目设置中, :libraries:lib1
和 :libraries:lib2
可以是Java项目, :app
Android项目可以使用它们的 jar
文件. 但是, 如果你想共享Android API或使用Android风格的资源, 这些库不能使用普通的Java项目, 必须使用Android库项目.
一个库项目同普通Android项目相似, 但有一些区别. 由于构建库与构建应用不同, 所以会使用另外一种插件. 在内部两种插件会共享大部分相同的代码, 他们都是由同一个 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" }
以上代码创建了一个库项目, 并使用API 23来编译. SourceSets , build types 和依赖库的处理方式都是相同的, 因为他们在同一个应用项目, 并通过相同的方式进行自定义.
库项目的主要输出是一个 .aar包
(即android archive缩写). 它是编译后的代码(例如jar文件或.so文件)和资源(manifest, res, assets). 一个库项目可以生成一个测试apk来单独测试一个库. 它会使用相同的锚点任务( assembleDebug
, assembleRelease
), 因此使用的命令并没有什么区别. 除此之外, 库也可以表现的同应用项目一样. 库拥有构建类型和渠道, 并可以潜在生成多于一种版本的aar. 注意, 大部分的构建类型配置不适用于库项目. 但是你可以使用自定义 sourceSet
来在测试和非测试的情况下动态改变库所依赖的内容.
引用一个库和引用其他项目一样:
dependencies { compile project(':libraries:lib1') compile project(':libraries:lib2') }
注意:如果你有一个以上的库, 那么库的顺序会很重要.
默认情况下, 一个库只会发布他的 release 变量. 该变量可以被整个项目使用来引用这个库. 无论他们构建的是何种变量. 这是Gradle自身限制产生的一个临时限制, 我们正努力去除该限制. 你可以控制要发布何种变量:
android { defaultPublishConfig "debug" }
注意这个发布配置名称引用了完整的变量名. release 和 debug 仅适用于没有渠道的情况. 如果你希望在使用渠道时改变默认发布变量, 你可以这样写:
android { defaultPublishConfig "flavor1Debug" }
发布一个库的所有变量也是可以的. 我们计划对普通的项目对项目的依赖方式使用这种方式(例如上例), 但目前由于Gradle限制我们还无法做到(我们正在努力修复)
默认情况下, 发布所有变量是禁用的. 以下代码可以启用该功能:
android { publishNonDefault true }
发布多个变量意味着会发布多个aar文件, 而不是一个aar含有多个变量, 理解这点很重要. 每个aar包都含有一个单独的变量. 发布一个变量意味着将这个aar作为Gradle项目的输出. 这可以用于发布到maven仓库, 或者用于其他项目的依赖.
Gradle有一个概念是”默认artifact”. 可以通过如下写法实现:
dependencies { compile project(':libraries:lib2') }
若要创建一个依赖于另一个已发布的artifact, 你需要指定具体使用哪一个:
dependencies { flavor1Compile project(path: ':lib1', configuration: 'flavor1Release') flavor2Compile project(path: ':lib1', configuration: 'flavor2Release') }
重要:注意发布配置是一个完整变量, 包含了构建类型
重要:当启用非默认发布时, Maven发布插件会将这些额外变量作为额外包进行发布. 也就是说该方式并不能完全兼容maven仓库的发布. 你应该发布单独变量到仓库, 或是对所有内部项目依赖都设置相同的配置.
构建测试应用的功能已经集成到了应用项目. 目前不再需要创建单独的测试项目.
在1.1中加入了单元测试的支持, 请阅读本文 . 本节余下内容描述了构建一个单独测试APK并在真机(或模拟器)上使用”instumentation测试”.
正如之前提到的, main
目录下面是 androidTest
目录, 默认位于 src/androidTest/
.
@todo
@todo
如之前所说, check需要一个已连接的设备才能执行, 它通过一个叫做 connectedCheck
的锚点任务来执行. 这基于 connectedDebugAndroidTest
任务. 该任务执行以下内容:
assebleDebug
和 assebleDebugAndroidTest
) 如果连接了多个设备, 所有测试会在所有已连接设备上并行运行. 如果任一个设备上的任一测试失败, 整个构建都会失败
@todo
当运行单元测试时, Gradle输出一个HTML报告来查看结果. Android插件根据此构建并扩展了HTML报告, 使其汇总所有连接设备的结果. 所有的测试结果存储在 build/reports/androidTests/
. 可以通过如下代码配置输出路径:
android { ... testOptions { resultsDir = "${project.buildDir}/foo/results" } }
android.testOptions.resultsDir
的值通过 Project.file(String)
)来获得
@todo
你可以为具体的variant运行lint, 例如 ./gradlew lintRelease
, 也可以为所有variant运行lint, 例如 ./gradlew lint
. 单独运行则产生单独的报告. 你可以添加lintOptions部分来配置lint. 参见 DSL参考
android { lintOptions { // 关闭指定问题的检查 disable 'TypographyFractions','TypographyQuotes' // 开启指定问题的检查 enable 'RtlHardcoded','RtlCompat', 'RtlEnabled' // 只检查指定的问题 check 'NewApi', 'InlinedApi' } }
新构建系统的一个目的是为同一个应用创建不同的版本
主要有两种用例:
我们的目标是可以用相同的项目生成不同的apk.
product flavor定义了项目构建的自定义版本. 一个单独项目可以有多种flavor, 它可以改变所生成的应用
这种设计的目的是用于产生最小的区别. 如果问你”这些是相同的应用吗?”, 答案是”是”的话, 那么应该使用库项目.
Product flavor使用 productFlavors
DSL容器来声明:
android { .... productFlavors { flavor1 { ... } flavor2 { ... } } }
这会创建两种flavor, 名为 flavor1
和 flavor2
.
注意:flavor的名称不能与已存在的 Build Type
名称, 或是 androidTest
, test
重复.
正如我们之前所见, 每个构建类型都会生成一个新的apk. Product Flavor也有同样作用: 项目的输出会是所有 Build Type
和 product flavor
的组合. 形成的组合叫做 Build Variant
. 例如, 如果使用默认的 debug
和 release
构建类型, 上面的例子会生成以下Build Variant:
没有flavor的项目也有Build Variant, 它会默认使用 default
flavor.
每种flavor使用单独的括号配置:
android { ... defaultConfig { minSdkVersion 8 versionCode 10 } productFlavors { flavor1 { applicationId "com.example.flavor1" versionCode 20 } flavor2 { applicationId "com.example.flavor2" minSdkVersion 14 } } }
注意 android.productFlavors.*
对象和 android.defaultConfig
对象都是 ProductFlavor
类型的, 也就是说他们会共享相同的属性.
defaultConfig
为所有flavor提供了基本配置, 每种flavor可重写任意值. 在上面的例子中, 最终的配置结果是这样的:
flavor1
applicationId
: com.example.flavor1 minSdkVersion
: 8 versionCode
: 20 flavor2
applicationId
: com.example.flavor2 minSdkVersion
: 14 versionCode
: 10
通常情况下, Build Type配置会覆盖其他的配置. 例如, Build Type的 applicationIdSuffix
会添加到Product Flavor的 applicationId
之后. 这适用于某种配置在Build Type和Product Flavor都适用的情况. 这种情况需要具体分析. 例如, signingConfig
是其中一个可以同时配置的属性. 它既可以通过设置 android.buildTypes.release.signingConfig
为所有release包共享相同的 SigningConfig
, 也可以通过为每一个release包配置单独的 android.productFlavors.*.signingConfig
对象来分别使用各自的 SigningConfig
.
与 BuildType
相同, Product Flavor
也会通过 sourceSet
使用自己的代码和资源. 上面的例子创建了4个 sourceSet
:
android.sourceSets.flavor1
src/flavor1/
android.sourceSets.flavor2
src/flavor2
android.sourceSets.androidTestFlavor1
src/androidTestFlavor1
android.sourceSets.androidTestFlavor2
src/androidTestFlavor2
这些 sourceSet
会被构建的apk使用. 在构建一个单独的apk时, 以下规则会被使用:
src/*/java
)都在一起, 因为所有目录都会生成同一个输出 main
sourceSet. 最后, 与 Build Type 类似, Product Flavor 可以有他们自己的依赖. 例如, 如果使用flavor来分别生成一个广告和一个付费app, 其中一个flavor可以设置一个广告sdk, 另一个则不需要.
dependencies { flavor1Compile "..." }
在这个场景下, 文件 src/flavor1/AndroidManifest.xml
可能需要加入网络权限.
还会为每个Variant创建额外的serceset:
android.sourceSets.flavor1Debug
src/flavor1Debug/
android.sourceSets.flavor1Release
src/flavor1Release/
android.sourceSets.flavor2Debug
src/flavor2Debug/
android.sourceSets.flavor2Release
src/flavor2Release/
以上这些比build type的sourceSet拥有更高的优先级, 并允许在variant级别进行自定义
每个 Build Type
都会创建自己的 assemble<name>
任务, 但 Build Variant
是 Build Type
和 Product Flavor
的结合
当使用 Product Flavor 时, 会创建更多assemble类型的任务. 有如下这下:
assemble<Variant Name>
assemble<Build Type Name>
assemble<Product Flavor Name>
assembleFlavor1Debug
第二个允许根据给定的Build Type构建所有APK. 例如 assembleDebug
会构建 Flavor1Debug
和 Flavor2Debug
variant
第三个匀速根据指定flavor构建所有apk. 例如 assembleFlavor1
会构建 Flavor1Debug
和 Flavor1Release
variant.
assemble
任务会构建所有可能的variant.
在某些情况下, 你可能想要为同一个app根据多种要求创建多种版本的app.
例如, 在Google Play所支持的multi-apk可以支持4中不同的过滤器.
为每种过滤器创建不同的apk可以通过使用多种Product Flavor实现.
假设一个游戏会有演示版本和付费版本, 并且希望使用multi-apk支持的ABI锅炉汽. 3种ABI和2个版本的应用, 需要生成6个apk
然而, 付费版本的代码对于3种ABI是相通的, 所以简单的创建6个flavor是不合适的.
此外, 现在有2种flavor, variant应该自动构建所有可能的组合.
这一功能可以通过Flavor Dimension实现. Flavor被指定为一个具体的dimension:
android { ... flavorDimensions "abi", "version" productFlavors { freeapp { dimension "version" ... } paidapp { dimension "version" ... } arm { dimension "abi" ... } mips { dimension "abi" ... } x86 { dimension "abi" ... } } }
android.flavorDimensions
数组定义了可能的dimension, 同时也定义了顺序. 每个定义的 Product Flavor
都被指定给了一个dimension
通过以下dimension定义的 Product Flavor [freeapp, paidapp] 和[x86, arm, mips]和[debug, release] Build Type , 会创建以下的build variant:
android.flavorDimensions
定义的dimension的顺序非常重要.
每个variant通过多个 Product Flavor 对象进行配置:
android.defaultConfig
dimension的顺序会决定哪个flavor会覆盖其他flavor, 当flavor中的某个值替换了低级别flavor中的值的情况下, 对于resource的影响还是比较重要的.
先定义的flavor具有更高的优先级. 所以在本例中:
abi > version > defaultConfig
多flavor项目同时会有额外的sourceset, 与variant sourceset类似, 但不会包括build type:
android.sourceSets.x86Freeapp
src/x86Freeapp/
android.sourceSets.armPaidapp
src/arcPaidapp/
这允许在flavor组合级别进行自定义. 他们比基本的flavor sourceset用用更高的优先级, 但低于build type sourceset的优先级.
@todo
在编译时期, Android Studio会生成一个叫做 BuildConfig
的类, 它包含了构建具体variat的常量值. 你可以在不同的variant中通过检查这些常亮来改变行为, 例如:
private void javaCode(){ if (BuildConfig.FLAVOR.equals("paidapp")) { doIt(); } else { showOnlyInPaindAppDialog(); } }
以下是BuildConfig包含的值:
boolean DEBUG
: 如果构建时可调式的 int VERSION_CODE
String VERSION_NAME
String APPLICATION_ID
String BUILD_TYPE
: build type的名称, 例如”release” String FLAVOR
: flavor名称, 例如: “paidapp” 如果项目使用flavor dimension, 会生成额外的值:
String FLAVOR = "armFreeapp"
String FLAVOR_abi = "arm"
String FLAVOR_version = "freeapp"
当你增加了dimension和flavor, 你可能会创建一些没有意义的variant. 例如, 你可能定义了一个flavor来使用web api, 另一个flavor使用写死的假数据用于快速测试. 第二种flavor只在开发时有用. 你可以使用 variantFilter
闭包来删除这个variant:
android { productFlavors { realData fakeData } variantFilter { variant -> def names = variant.flavors*.name if (names.contians("fakeData") && variant.buildType.name == "release") { variant.ignore = true } } }
通过以上配置, 你的项目只有3个variant:
realDataDebug
realDataRelease
fakeDataDebug
可以参见 DSL手册
了解更多 variant
的属性
ProGuard插件在Android插件中会自动应用, 如果 Build Type 通过配置 minifyEnabled 属性启用了ProGuard后, 任务会自动创建.
android { buildTypes { release { minifyEnalbled true proguardFile getDefualtProguardFile('proguard-android.txt') } } productFlavors { flavor1 { } flavor2 { proguardFile 'some-other-rules.txt' } } }
Variant会使用build type和product flavor中声明的所有规则文件
有2个默认的规则文件:
他们位于SDK中. 使用 getDefualtProguardFile()
可以返回文件的完整路径.
你可以在构建时自动删除没有使用的资源文件. 可以参见 资源压缩 文档
基本的Java项目有多个任务一起工作来创建一个输出.
classes
任务用于编译Java源代码. 通过在脚本中使用 classes
可以方便地访问 build.gradle
. 这是 project.tasks.classes
的快捷方式
在Android项目中, 这可能会复杂一些. 因为可能会有很大数量的相同任务, 并且他们的名字是根据 Build Type 和 Product Flavor 来生成的.
为了修复这一问题, 在 android
对象中有两个属性:
applicationVariants
(只用于app插件) labraryVariants
(只用于library插件) testVariants
(用于两种插件)
他们会返回 ApplicationVariant
, LibraryVariant
, 和 TestVariant
相应的 DomainObjectColletion
注意, 访问任意的collection都会触发所有任务的创建. 也就是说在访问collection后不能再进行配置.
DomainObjectCollection
为所有项目提供了直接访问或通过过滤器访问的方式
android.applicationVariants.all { variant -> ... }
三种variant类都共享以下属性:
属性名 | 属性类型 | 描述 |
---|---|---|
name | String | variat名称. 保证唯一. |
description | String | 人类可读的variant描述. |
dirName | String | variant子目录名称. 保证唯一. 可能会有多个目录, 例如“debug/flavor1” |
baseName | String | variant输出的基本名称. 保证唯一 |
outputFile | File | variant输出. 是读/写属性 |
processManifest | ProcessManifest | 处理Manifest的任务. |
aidlCompile | AidlCompile | 编译AIDL文件的任务. |
renderscriptCompile | RenderscriptCompile | 编译Renderscript文件的任务. |
mergeResources | MergeResources | 合并资源的任务 |
mergeAssets | MergeAssets | 合并assets的任务 |
processResources | ProcessAndroidResources | 处理和编译资源的任务 |
generateBuildConfig | GenerateBuildConfig | 生成BuildConfig类的任务 |
javaCompile | JavaCompile | 编译Java代码的任务 |
processJavaResources|Copy|处理Java资源的任务|
|assemble|DefaultTask|该variant的组装锚点任务|
ApplicationVariant
类增加了如下属性:
属性名 | 属性类型 | 描述 |
---|---|---|
buildType | BuildType | variant的BuildType |
productFlavors |
List |
variant的ProductFlavors. 可以为空单永不为null. |
mergedFlavor | ProductFlavor | android.defaultConfig和variant.productFlavors的合并 |
signingConfig | SigningConfig | 该variant使用的SigningConfig对象 |
isSigningReady | boolean | true如果variant拥有所有签名需要的信息 |
testVariant | BuildVariant | 测试该variant的TestVariant |
dex | Dex | dex代码的任务. 如果variant是一个libaray则为null |
packageApplication | PackageApplication | 构建最终apk的任务. 如果variant是一个library则为null |
zipAlign | ZipAlign | zipalign apk的任务. 如果variant是一个library或apk无法签名时为null |
install | DefaultTask | 安装任务, 可以为null |
uninstall | DefaultTask | 卸载任务 |
LibraryVariant
类增加了以下属性:
属性名 | 属性类型 | 描述 |
---|---|---|
buildType | BuildType | variant的BuildType |
mergedFlavor | ProductFlavor | defaultConfig值 |
testVariant | BuildVariant | 测试该variant的Build Variant |
packageLibrary | Zip | 打包Library的arr文件的任务. 如果不是library时为null |
TestVariant
增加了以下属性:
属性名 | 属性类型 | 描述 |
---|---|---|
buildType | BuildType | variant的BuildType |
productFlavors |
List |
variant的ProductFlavors可以为空但永远不为null |
mergedFlavor | ProductFlavor | android.defaultConfig和variant.productFlavors的合并 |
signingConfig | SigningConfig | 该variant使用的SigningConfig对象 |
isSigningReady | boolean | true如果该variant有所有签名需要的信息 |
testedVariant | BaseVariant | 该TestVariant测试用的BaseVariant |
dex | Dex | dex代码的任务. 如果variant是一个library则为null. |
packageApplication | PackageApplication | 构建最终apk的任务. 如果variant是一个library则为null |
zipAlign | ZipAlign | zipalign apk的任务. 如果variant是一个library或apk无法签名则为null |
install | DefaultTask | 安装任务, 可以为null |
uninstall | DefaultTask | 卸载任务 |
connectedAndroidTest | DefaultTask | 在已连接的设备上运行android测试的任务 |
providerAndroidTest | DefaultTask | 使用扩展API运行android测试的任务 |
Android具体任务类型的API:
ProcessManifest
File manifestOutputFile
AidlCompile
File sourceOutputDir
RenderscriptCompile
File sourceOutputDir
File resOutputDir
MergeResources
File outputDir
MergeAssets
File outputDir
ProcessAndroidResources
File manifestFile
File resDir
File assetsDir
File sourceOutputDir
File textSymbolOutputDir
File packageOutputFile
File proguardOutputFile
GenerateBuildConfig
File sourceOutputDir
Dex
File outputFolder
PackageApplication
File resourceFile
File dexFile
File javaResourceDir
File jniDir
File outputFile
ZipAlign
File inputFile
File outputFile
每种任务类型的api会由于Gradle工作方式和Android插件的设置而有所限制
首先, Gradle只希望配置输入/输出位置和可能的可选标志. 所以在此任务只在输入/输出定义.
第二, 大部分任务的输出都是不重要的, 它们大部分都来自 srouceSets , Build Type , Product Flavor 的混合值. 为了保持构建文件的简洁易懂, 我们的目标是让开发者通过DSL来修改构建, 而不是深入输出和任务选项来修改它们.
注意, 除了ZipAlign任务, 其他所有任务类型都需要设置私有数据才能正常工作. 这意味着不能手动创建这些类型的任务.
你可以使用 compileOptions
块来选择编译器的语言级别. 默认会使用 compileSdkVersion
的值
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 targetCompatitility JavaVersion.VERSION_1_6 } }