进入2017年,Android Studio 版本更新至3.0,连带着com.android.tools.build:gradle 工具也升级到了3.0.0,在3.0.0中使用了最新的Gralde 4.0 里程碑版本做为gradle 的编译版本,该版本gradle编译速度有所加快,更加欣喜的是,彻底支持Java 8。固然,对于Kotlin的支持在这个版本也更加完善。进入12月份,谷歌又将 com.android.tools.build:gradle 版本更新到了3.0.1(Gradle 4.1),修复了一些 bug 并提高了启动速度,在这里咱们直接拿最新的4.1版本的特性做为参照对象。android
在 com.android.tools.build:gradle 3.0.0(即Gradle 4.0)版本中,compile关键字已经明确写明废弃了,可是google官方文档上说“还会保留一段时间,直到下个比较大的gradle tools版本发布”,因此如今使用compile暂时不会报错,取而代之的是 api 关键字(做用等同于compile关键字)和 implementaion 关键字。具体修改规则修改以下:express
废弃的缘由其实提及来很简单——就是为了加快工程的构建。api
为了理解老版本Gradle 3.X构建系统的限制,这里假设有个工程使用了多层module依赖方式。如图所示:安全
对于最底部的基础module
,其将会有两种可能的变化:app
module
对外暴露的接口。module
对外暴露的接口。(本module
指的是调用dependency
的module
)注意:全部须要从新编译的module将会在如下用红色标出。eclipse
当本module依赖的ib(也能够是module)发生变化时,因为本module对外暴露的接口并不发生变化,在构建工程时gradle将会只从新编译本module,全部依赖于本module的module并不会发生编译。这种状况是没什么问题的。如图所示:jvm
当本module依赖的ib(也能够是module)发生变化时,本module向外暴露的接口发生了变化,那么全部直接依赖于本module的module将不得不从新编译。ide
接下来,这些上层module可能经过其自己的接口对外暴露了底层module的部份内容,即意味着本module暴露的接口也发生了变化,这会使得依赖于上层module的上上层module也须要从新编译。这就致使了一个连锁效应,所以,为了绝对的安全起见,gradle将不得不从新编译整个工程,使得编译时间变得较长。如图所示:工具
一点代码的改动可能会引发整个工程的从新编译,对构建效率的影响显而易见,而实际上Gradle 4.0以前的版本的确是如此处理的,根本缘由就是gradle压根不知道暴露的接口能够经过一个接一个的依赖传递影响整个工程。测试
最新版的Gradle plugin
须要你指出一个module
的接口是否对外暴露其依赖lib的接口。基于此,可让项目构建时,gradle
能够判断哪一个须要从新编译。所以,老版本的构建关键字compile被废弃了,而是改为了这两个:
之前咱们要建立不一样版本的apk或者aar是使用productFlavor
和buildType
这两个维度。若是须要更多维度就须要Gradle Android的新机制 dimension(维度):
//定义两个风味维度 flavorDimensions "api", "mode" productFlavors { demo { //指定风味维度 dimension "mode" ... } full { dimension "mode" ... } minApi24 { dimension "api" minSDKVersion '24' versionNameSuffix "-minApi24" } minApi23 { dimension "api" minSDKVersion '23' versionNameSuffix "-minApi23" } minApi21 { dimension "api" minSDKVersion '21' versionNameSuffix "-minApi21" } }
如上,配置完后,Gradle建立的构建变体数量等于每一个风味维度(flavorDimension)中的风味(productFlavor)数量的乘积再乘以你配置的构建类型数量。以上面的构建配置为例,Gradle 能够建立的构建变体数量为:3(api) x 2(mode) x 2(release和debug) = 12。
在 Gradle 为每一个构建变体或对应 APK 命名时,属于较高优先级风味维度的产品风味首先显示,以后是较低优先级维度的产品风味,再以后是构建类型。
以上面的构建配置为例,Gradle 可使用如下命名方案建立总共 12 个构建变体:
构建变体:minApi24, minApi23, minApi21[Debug, Release]
对应 APK:app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
例如构建变体:minApi24DemoDebug,对应 APK:app-minApi24-demo-debug.apk
这样,你们大体知道这个维度究竟是个什么东西了吧。就是咱们之前构建变体只有productFlavor
和buildType
这两个维度,如今能够定义任意多个维度了。
若是有些变体不想要,能够经过setIgnore(true)
过滤掉他,这样编译也快一些:
android { ... variantFilter { variant -> def names = variant.flavors*.name // To check for a certain build type, use variant.buildType.name == "<buildType>" if (names.contains("minApi21") && names.contains("demo")) { // Gradle ignores any variants that satisfy the conditions above. setIgnore(true) } } ... }
以前咱们只有sit
和pro
两个flavor,那么咱们只须要经过flavorDimensions
定义一个名为mode
的dimension,而后给这两个flavor都设置dimension
为mode
便可。这个dimension的名字也能够本身起,好比能够叫env
表示是生产环境和测试环境。
若是以前没有定义productFlavor就没有必要修改了。
优先级
构建变体 > 构建类型 > 产品风味 > 主源集 > 库依赖项
其中:
构建变体
就是多个维度最终产生的组合拳.
咱们把buildType
也做为一个dimension,他称为构建类型
.
而productFlavor中定义的dimension为产品风味
.
主源集就是main
目录下面的资源了
库依赖项固然是各类依赖库了.
例如:
1. src/demoDebug/(构建变体源集)
2. src/debug/(构建类型源集)
3. src/demo/(产品风味源集)
4. src/main/(主源集)
若是依赖工程,不须要像之前那样:
sitReleaseCompile project(path:':lib',configuration:'sitRelease') proReleaseCompile ... ...
只须要下方写法便可:
compile project(":lib")
他会自动按照构建类型去寻找。那要是找不到呢? 好比对应的lib中没有对应的构建类型怎么办?
好比咱们给Module app的buildType增长一个jniDebuggable类型以下:
jniDebug { jniDebuggable true }
而在app所依赖的lib中没有这个构建类型,那自动依赖就会报错。 这时候,咱们能够指定matchingFallbacks
表示找不到对应的依赖时能够按配置中指定的顺序找到第一个可用的,以下:
jniDebug { jniDebuggable true matchingFallbacks =['debug','release'] }
注意当依赖的库中包含了Module App没有的构建类型,则不会出现上述问题。
例如,主Module App和库中都包含了一个mode的风味维度,咱们的App中指定mode维度的是free和paid风味,而库中指定mode维度的是demo和paid风味,这时候咱们就能够用`matchingFallbacks 来为App中的free指定能够替换的匹配项。以下:
android { defaultConfig{ // Do not configure matchingFallbacks in the defaultConfig block. // Instead, you must specify fallbacks for a given product flavor in the // productFlavors block, as shown below. } flavorDimensions 'mode' productFlavors { paid { dimension 'mode' // Because the dependency already includes a "paid" flavor in its // "mode" dimension, you don't need to provide a list of fallbacks // for the "paid" flavor. } free { dimension 'mode' // Specifies a sorted list of fallback flavors that the plugin // should try to use when a dependency's matching dimension does // not include a "free" flavor. You may specify as many // fallbacks as you like, and the plugin selects the first flavor // that's available in the dependency's "mode" dimension. matchingFallbacks = ['demo', 'trial'] } } }
上述状况中,若是说库中包含了一个主Module App没有的产品风味,则不会出现上述问题。
例如,库中声明了一个minApi的风味维度,可是你的App中只有mode维度,所以当你要构建freeDebug这个变种版本的App时,插件就不知道你是想用minApi23Debug仍是用minApi25Debug变种版本的库,这时候咱们能够在主Module App中的defaultConfig代码块经过配置missingDimensionStrategy来让插件从丢失的维度中指定默认的风味,固然你也能够在productFlavors代码块中覆盖先前的选择,所以每个风味均可觉得丢失的维度指定一个不一样的匹配策略。
android { defaultConfig{ // Specifies a sorted list of flavors that the plugin should try to use from // a given dimension. The following tells the plugin that, when encountering // a dependency that includes a "minApi" dimension, it should select the // "minApi23" flavor. You can include additional flavor names to provide a // sorted list of fallbacks for the dimension. missingDimensionStrategy 'minApi', 'minApi23', 'minApi25' } flavorDimensions 'mode' productFlavors { free { dimension 'mode' // You can override the default selection at the product flavor // level by configuring another missingDimensionStrategy property // for the "minApi" dimension. missingDimensionStrategy 'minApi', 'minApi25', 'minApi23' } paid {} } }
这里指的是若是若是依赖的工程中有这个minApi维度而主模块没有这个维度,则按照这个顺序选择依赖的flavor,好比优先选择minApi,若是没有minApi再选择minApi23.
当你的主Module App中包含了一个库中依赖项没有的风味维度时,则不会出现上述问题。例如,当库中依赖项不包含abi这个维度时,freeX86Debug版本将会使用freeDebug版本的依赖。
尤为注意的是咱们重命名打包的APK文件,以及输出路径。变化前:
applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk')) { if (variant.buildType.name == 'lotteryTest') { def fileName = "myApp_v${defaultConfig.versionName}_${releaseTime()}.apk" output.outputFile = new File(outputFile.parent, fileName) } } } }
变化后:
applicationVariants.all { variant -> variant.outputs.all { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk')) { if (variant.buildType.name == 'lotteryTest') { def fileName = "myApp_v${defaultConfig.versionName}_${releaseTime()}.apk" outputFileName = new File(fileName) } } } }
即咱们须要修改each() 和 outputFile() 方法为 all() 和 outputFileName
在迁移的过程当中,若是发现因为aapt2致使的异常,能够在gradle.properties中加入:
android.enableAapt2=false
Gradle带来全新的Java8支持方案desugar,启用十分简单,只须要配置下面代码:
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }
若是你不想使用,也能够禁用,能够在gradle.properties中加入:
android.enableDesugar=false
记得删除上面的兼容Java8代码。
android { ... defaultConfig { ... // Remove this block. jackOptions { enabled true ... } } }
在project中的build.gradle中移除:
buildscript { ... dependencies { // Remove the following dependency. classpath 'me.tatarka:gradle-retrolambda:<version_number>' } }
Module级build.gradle文件:
// Remove the following plugin. apply plugin: 'me.tatarka.retrolambda' ... // Remove this block after migrating useful configurations. retrolambda { ... // If you have arguments for the Java VM you want to keep, // move them to your project's gradle.properties file. jvmArgs '-Xmx2048m' }