03.AGP升级
为什么要选择 VersionCatalog 来做依赖管理?AGP 升级到 3.x
官网文档:
https://developer.android.com/studio/releases/gradle-plugin.html#revisions
升级到 AGP3.0.0+
https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html
新特性:
- Java8 支持
- 新的依赖匹配机制
- aapt2
1、升级 android gradle plugin(AGP) 和 gradle wrapper 到最新版
- 升级 android gradle plugin(AGP) 到
3.0.0
及以上
classpath ‘com.android.tools.build:gradle:3.0.1’
- 升级 gradle wrapper 到
4.1
及以上
1
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
- 添加 google() 仓库
1
2
3
4
repositories{
google()
// jcenter()、maven()
}
2、新的依赖配置
依赖关键字的改变
- api:对应之前的 compile 关键字,功能一模一样。会传递依赖,导致 gradle 编译的时候遍历整颗依赖树
- implementation:对应之前的 compile,与 api 类似,关键区别是不会有依赖传递
- compileOnly:对应之前的 provided,依赖仅用于编译期不会打包进最终的 apk 中
- runtimeOnly:对应之前的 apk,与上面的 compileOnly 相反,作用于运行时,编译期没有
关于 implementation 与 api 的区别,主要在依赖是否会传递上。如:A 依赖 B,B 依赖 C,若使用 api 则 A 可以引用 C,而 implementation 则不能引用
这里更推荐用 implementation,一是不会间接的暴露引用,清晰知道目前项目的依赖情况;二是可以提高编译时依赖树的查找速度,进而提升编译速度。详见 SO 的这个回答,讲得非常详细了:【stackoverflow】gradle-implementation-vs-api-configuration
新的配置 | 旧的配置 | 行为表现 |
---|---|---|
implementation | compile | 你的 module 配置了 implementation 依赖,它让 gradle 知道这个 module 不想让其他的 module 依赖泄露了,也就是说,这个依赖只能在直接依赖可用,间接依赖不可用。这种可以有效减少编译时间,一般 app 和 test 都应该配置这种。不能依赖传递 |
api | compile | 行为和之前的 compile 一样,可以进行依赖传递; 只在 library module 中使用;过多的 api 依赖会增大编译时间 |
compileOnly | provided | 只在编译时添加依赖,不会添加到输出 apk 包 |
runtimeOnly | apk | 只在 apk 输出时添加依赖。在编译时不会添加 |
出现这种情况: 将 library 的 implementation 改成 api 即可
Error:Execution failed for task ‘:app :preDebugBuild’.
Android dependency ‘com.android.support:support-v4’ has different version for the compile (23.1.1) and runtime (27.1.0) classpath. You should manually set the same version via DependencyResolution
3、android annotation processor config
- 移除 android-apt 相关的 plugin,如:
1
classpath 'com.neenbedankt.gradle.plugins:android-apt :1.8'
apt
采用annotationProcessor
替代
1
annotationProcessor 'com.android.databinding:compiler :3.0.0'
- 如果有用到类似 Realm 这种第三方的 plugin,确保升级到最新版试试(旧版的 Realm 用的还是 android-apt),突然发现升级到最新版后 api 接口被改了
1
2
3
4
5
6
7
8
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "io.realm:realm-gradle-plugin :4.2.0"
}
}
- Use the annotation processor dependency configuration
在之前的 agp 插件版本,在compile
的依赖路径中,compile classpath
自动地添加到processor classpath
中。也就是说,你添加了一个 annotation processor 到 compile classpath 中。这就影响了性能,添加了大量的不必要的依赖到 processor。
当用 AGP3.x 时,你必须添加 annotation processors 到 processor classpath 当用annotationProcessor
依赖配置时:
1
2
3
4
dependencies {
// ...
annotationProcessor 'com.google.dagger:dagger-compiler :<version-number>'
}
关闭 annotation processor 错误检测,这个特性不久就来会关闭
1
2
3
4
5
6
7
8
9
10
11
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath false
}
}
}
}
如,tinker 报错,设置 includeCompileClasspath true
即可
1
2
3
4
5
Error:Execution failed for task ':app :javaPreCompileDebug'.
> Annotation processors must be explicitly declared now. The following dependencies on the compile classpath are found to contain annotation processor. Please add them to the annotationProcessor configuration.
- tinker-android-anno-1.7.11.jar (com.tencent.tinker:tinker-android-anno :1.7.11)
Alternatively, set android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true to continue with previous behavior. Note that this option is deprecated and will be removed in the future.
See https://developer.android.com/r/tools/annotation-processor-error-message.html for more details.
4、生成 APK 文件名属性 outputFile 变为只读
1
2
3
4
5
6
7
applicationVariants.all { variant ->
variant.outputs.each { output ->
def file = output.outputFile
def apkName = 'xxx-xxx-xxx-signed.apk'
output.outputFile = new File(file.parent, apkName)
}
}
报错:
1
2
Error:(233, 0) Cannot set the value of read-only property 'outputFile' for ApkVariantOutputImpl_Decorated{apkData=Main{type=MAIN, fullName=devDebug, filters=[]}} of type com.android.build.gradle.internal.api.ApkVariantOutputImpl.
< a href=" ">Open File</ a>
由于 outputFile 属性变为只读,需要进行如下修改,直接对 outputFileName 属性赋值即可:
1
2
3
4
5
6
applicationVariants.all { variant ->
variant.outputs.all {
def apkName = 'xxx-xxx-xxx-signed.apk'
outputFileName = apkName
}
}
5、flavor
1) 渠道需要声明 flavor dimensions
报错:
1
Error:All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com/r/tools/flavorDimensions-missing-error-message.html
也就是也是 flavor 渠道都必须归属一个 dimension,若只有一个 dimension,渠道中可以不写 dimension 属性,默认分配到该维度。直接添加一个默认的维度即可:如 flavorDimensions "dimension"
,当然 flavorDimensions 也可以设置多个维度,详见官方实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Specifies two flavor dimensions.
flavorDimensions "mode", "minApi"
productFlavors {
free {
// Assigns this product flavor to the "tier" flavor dimension. Specifying
// this property is optional if you are using only one dimension.
dimension "mode"
...
}
paid {
dimension "mode"
...
}
minApi23 {
dimension "minApi"
...
}
minApi18 {
dimension "minApi"
...
}
}
2) 库多 variant 依赖方式的修改 (variant=flavor*debug
)
AGP3.x 引入了新的 variant 自动匹配机制,也就是说 app 的 flavorDebug variant 会自动匹配 library 的 flavorDebug variant.
回顾一下旧的方式,如果 app 在某个 variant 下需要依赖 library 相应的类型,需要按照下面的方式声明依赖:
1
2
3
4
5
6
dependencies {
// This is the old method and no longer works for local
// library modules:
debugCompile project(path: ':library', configuration: 'debug')
releaseCompile project(path: ':library', configuration: 'release')
}
新的方式,gradle 会自动感知并匹配对应的 variant(前提是 app 与 library 中有对应的 variant 类型):
1
2
3
4
5
6
7
dependencies {
// Instead, simply use the following to take advantage of
// variant-aware dependency resolution. You can learn more about
// the 'implementation' configuration in the section about
// new dependency configurations.
implementation project(':library')
}
3) 处理 app 与 lib 的依赖匹配问题
上面我们了解到新的 variant 匹配机制,但若 app 或 library 中不存在对应的 variant 类型呢?匹配将如何进行?下面列出了可能出现的几种情形:
- app 有某个 build type 但 library 没有,可以通过
matchingFallbacks
属性来设置回退策略,提供可能的匹配列表,会选择第一个可用的,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// In the app's build.gradle file.
android {
buildTypes {
debug {}
release {}
staging {
// Specifies a sorted list of fallback build types that the
// plugin should try to use when a dependency does not include a
// "staging" build type. You may specify as many fallbacks as you
// like, and the plugin selects the first build type that's
// available in the dependency.
matchingFallbacks = ['debug', 'qa', 'release']
}
}
}
若希望可以针对 app 的每个 build type 都执行相同的回退策略(例如我们大量的 library 只有一个 release 的 build type),则可以使用批量指令:
1
2
3
buildTypes.all { type ->
type.matchingFallbacks = ['release']
}
注意:在该情景下,若 library 中有某个 build type 但 app 没有,不会对 app 有任何影响
- 在同一个 dimension 维度下,如:tier,若 app 中有某个 flavor 但 library 却没有
同样通过matchingFallbacks
属性来设置回退策略:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// In the app's build.gradle file.
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 'tier'
productFlavors {
paid {
dimension 'tier'
// Because the dependency already includes a "paid" flavor in its
// "tier" dimension, you don't need to provide a list of fallbacks
// for the "paid" flavor.
}
free {
dimension 'tier'
// 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 "tier" dimension.
matchingFallbacks = ['demo', 'trial']
}
}
}
注意:在该情景下,若 library 中有某个 flavor 但 app 却木有,不会对 app 有任何影响
- library 中有某个 dimension 维度,但 app 中却没有
可以通过 missingDimensionStrategy 属性来设置选择策略:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// In the app's build.gradle file.
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
// "minApi18" flavor. You can include additional flavor names to provide a
// sorted list of fallbacks for the dimension.
missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
}
flavorDimensions 'tier'
productFlavors {
free {
dimension 'tier'
// You can override the default selection at the product flavor
// level by configuring another missingDimensionStrategy property
// for the "minApi" dimension.
missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
}
paid {}
}
}
说明:其中 missingDimensionStrategy 属性的第一个值为 dimension 维度,后面的 Strings 为该维度下的渠道 flavors。
在该情景下,若 app 中有某个 dimension 维度,但 library 中却没有,不会对 app 有任何影响
- 若 library 没有任何 dimension 和 flavor,则不需 app 做任何 flavor 的回退处理~
其实诸如 dimension 的声明以及提供匹配回退策略都是为了实现精确的 variant 匹配
6、对 Java8 支持
AGP3.x,jack
被官方弃用了,用了最新的 desugar
方案替代。
禁用特性
1
android.enableDesugar=false
Java8 支持的特性:
- lambda 表达式
- Method References
- Type Annotations
- Default and static interface methods
- Repeating annotations
stream 及 function 包下的 api 只能在 api24+ 以上才可以使用
AGP7.0
VersionCatalog 版本管理
AGP7.0 脚本变更
[[03.Gradle 自定义插件#新版本Gradle配置更改]]
Enable configuration caching (截止 AGP7.x 还是实验阶段)
Gradle 生命周期分为 Initialization、Configuration 和 Execution Phase;Task Execution 已经有缓存了,但 Configuration 阶段还没有缓存,通过下面配置就可以启动 Configuration cache:
1
2
3
# configuration cache
org.gradle.unsafe.configuration-cache=true
org.gradle.unsafe.configuration-cache-problems=warn
需要 task 适配 Configuration Cache
适配 configuration-cache
遇到的问题
某些库的 task 未适配 configuration-cache,会报错:
1
2
3
4
5
6
7
8
9
10
11
12
Configuration cache state could not be cached: field 'actions' from type 'org.gradle.api.DefaultTask': error writing value of type 'java.util.ArrayList'
> Configuration cache state could not be cached: field 'closure' from type 'org.gradle.api.internal.AbstractTask$ClosureTaskAction': error writing value of type 'com.chaquo.python.PythonPlugin$_createAssetsTasks_closure21$_closure46'
> Configuration cache state could not be cached: field 'variant' from type 'com.chaquo.python.PythonPlugin$_createAssetsTasks_closure21$_closure46': error writing value of type 'groovy.lang.Reference'
> Configuration cache state could not be cached: field 'value' from type 'groovy.lang.Reference': error writing value of type 'com.android.build.gradle.internal.api.ApplicationVariantImpl'
> Configuration cache state could not be cached: field 'testVariant' from type 'com.android.build.gradle.internal.api.ApplicationVariantImpl': error writing value of type 'com.android.build.gradle.internal.api.TestVariantImpl'
> Configuration cache state could not be cached: field 'variantData' from type 'com.android.build.gradle.internal.api.TestVariantImpl': error writing value of type 'com.android.build.gradle.internal.variant.TestVariantData'
> Configuration cache state could not be cached: field 'testedVariantData' from type 'com.android.build.gradle.internal.variant.TestVariantData': error writing value of type 'com.android.build.gradle.internal.variant.ApplicationVariantData'
> Configuration cache state could not be cached: field 'artifacts' from type 'com.android.build.gradle.internal.variant.ApplicationVariantData': error writing value of type 'com.android.build.api.artifact.impl.ArtifactsImpl'
> Configuration cache state could not be cached: field 'storageProvider' from type 'com.android.build.api.artifact.impl.ArtifactsImpl': error writing value of type 'com.android.build.api.artifact.impl.StorageProviderImpl'
> Configuration cache state could not be cached: field 'fileStorage' from type 'com.android.build.api.artifact.impl.StorageProviderImpl': error writing value of type 'com.android.build.api.artifact.impl.TypedStorageProvider'
> Configuration cache state could not be cached: field 'singleStorage' from type 'com.android.build.api.artifact.impl.TypedStorageProvider': error writing value of type 'java.util.LinkedHashMap'
> java.util.ConcurrentModificationException (no error message)
This occurred due to the Cache of Build Gradle In the cache, some Libraries are not Cached so it Gives the Error
Fixed:
1
2
// gradle.properties
org.gradle.unsafe.configuration-cache=false
Transform 7.2 的废弃
[[02.AGP之Transform#7.2 GAP Transform适配]]
pluginManagement 适配
[[03.Gradle 自定义插件#Plugin Management]]
AGP8.0
AGP8.0 变化
- 要有 namespace
- buildFeature 需要增加 buildConfig true 才会生成 BuildConfig
R class field 不是常量了
- 不是常量,对编译速度有提升,更好的资源 shrink
- switch case 需要替换成 if else 或 when
- AGP8.0 及以上默认行为
- 保持旧的行为,在 gradle.properties:
1
android.nonFinalResIds=false