Gradle Configuration
环境
Gradle:4.4.1
Configuration 介绍
通过声明的方式定义了一组依赖。Gradle 通过声明的方式查找对应依赖产物和他们的自身的依赖产物。它代表一组文件。只是这些文件是根据声明信息从本地或远程仓库中获取的。
Configuration 状态
状态分为三个 UNRESOLVED(未解析),RESOLVED(解析成功)/ RESOLVED_WITH_FAILURES(解析失败)
从未解析到解析成功或解析失败主要涉及两个过程,一 解析依赖图,二 获取产物。
一 解析依赖图
主要的功能是使用广度优先算法遍历解析依赖图。同时解决依赖图中相同依赖的版本冲突。
解析冲突策略
解决版本冲突方式主要使用 3 种策略:
1 版本优先:
LatestModuleConflictResolver
一个版本号版本被分为2两部分。数字版本号
+限定版本号
。数字版本号
开始的数字的部分。剩下的为限定版本号
在通常版本号分为
eg:1.2.3-bate3,[1,2,3] 为数字版本号
,[bate,3] 为 限定版本号
比较原则
数字版本号
比较大小,逐个比较数字部分,数字大的版本号大
eg: 1.2.3 > 1.1.19 ,1.2.3.1 > 1.2.3限定版本号
比较大小,数字 > final > release > rc > (任意非数字字符) > dev 。任意非数字字符比较方式是逐个比较 Char 的 ASCII码值。数字版本号
大的版本号大。数字版本号
相同,没有限定版本号
大于有限定版本号
。
eg:1.2.3 >1.2.3-beta数字版本号
相同,限定版本号
大的版本号大。
eg: 1.2.3-final > 1.2.3-release > 1.2.3-rc3 > 1.2.3-rc > 1.2.3-beta > 1.2.3-dev数字版本号
相同,非 SNAPSHOT 大于 SNAPSHOT版本。
eg:1.2.3-beta > 1.2.3-SNAPSHOT(注意全部大小)- 版本声明顺序不影响版本号的比较。
- 一个依赖版本被选中,那么它的父节点也要被选中。
版本比较中的魔幻
Q:版本 0.1.1-20181030.154719-1 跟 0.1.1-beta 哪个版本比较新?
A:0.1.1-beta。 虽然数字版本号
相同,限定版本 [20181030,154719,1] 大于 [beta],但是 0.1.1-20181030.154719-1 满足正则(.+)-\\d{8}\\.\\d{6}-\\d+·
会被解析成 0.1.1-SNAPSHOT 中的 20181030.154719-1版本 , 所以 0.1.1-20181030.154719-1 是个 SNAPSHOT 版本。对应比较原则中的第6原则,非 SNAPSHOT 大于 SNAPSHOT版本。
Q:版本 0.1.1-20181030.154719-1 跟 0.1.1-SNAPSHOT 哪个版本比较新?
A: 0.1.1-20181030.154719-1
Q:1.2.1-SNAPSHOT VS 1.2.01
2
3
4
5
6情况A:
'com.dim.red:one:1.2.0'
'com.dim.red:one:1.2.1-SNAPSHOT'
情况B:
'com.dim.red:one:1.2.1-SNAPSHOT'
'com.dim.red:one:1.2.0'
1.2.0 跟 1.2.1-SNAPSHOT 哪个版本比较,谁比较新?情况 A 和情况 B 会有不同吗?
A: 通过比较原则中 1 和 6 。那么 1.2.1-SNAPSHOT 不管在情况 A 和情况 B 中都应该始终大于 1.2.0 。但是在 Gradle 在 4.4 - 4.6 版本中存在一个BUG。
将导致的问题是情况 A 1.2.0 版本大于 1.2.1-SNAPSHOT。 情况 B 1.2.1-SNAPSHOT 大于 1.2.0。具体的原因可以比对版本 4.4.1 和 4.10.2 的实现。
LatestModuleConflictResolver.select(details) (4.4.1)
LatestModuleConflictResolver.select(details)(4.10.2 )
Q:1
2
3
4
5
6
7
8
9
10
11
12情况A
com.dim.red:a:2.0.0
\--- com.dim.red:b:1.2.0
com.dim.red:c:1.0
\--- com.dim.red:a:2.2.0
\--- com.dim.red:b:1.0.0
情况B
com.dim.red:c:1.0.0
\--- com.dim.red:a:2.2.0
\--- com.dim.red:b:1.0.0
com.dim.red:a:2.0.0
\--- com.dim.red:b:1.2.0
依赖 com.dim.red:b 会选中什么版本?情况 A 和情况 B 会有不同吗?
A:通过比较原则中 7 和 8。所以应该是 c:1.0,a:2.2.0,b:1.0.0。
但是在 Gradle 的不同版本实现中表现却不一样。在 Gradle 4.9 以下情况 A 会选中版本 c:1.0.0,a:2.2.0,b:1.2.0。而情况 B 选中版本 c:1.0.0,a:2.2.0,b:1.0.0 这里明显是一个BUG。 4.9 修正这个BUG。原因在于 4.9 以下在版本比较中没有过滤掉父节点为空的版本。但是 4.9 的版本中存在一个新的BUG #7050 。该 BUG 在5.0-rc中修复。但是 Gradle 5.0 - rc 的版本还不兼容现有的 Android Gradle Plugin 3.2.1 版本。
2 严格模式
StrictConflictResolver
不允许出现版本冲突。当出现版本冲突的时候,需要手动强制声明锁定版本。
3 项目优先
ProjectDependencyForcingResolver
本地项目版本大于远程依赖版本。当没有远程或多个本地项目,使用版本优先算法进行比较。
强制声明锁定版本
声明以后,在发生版本冲突的时候会直接选择锁定版本。而不需要经过策略。
锁定版本方式有两种:1
2
3implementation ('com.google.code.gson:gson:2.8.5'){
force = true
}
或1
2
3
4
5configurations.all {
resolutionStrategy {
force 'com.google.code.gson:gson:2.8.5‘
}
}
策略设置
默认策略为版本优先。1
2
3
4
5
6
7configurations.all {
resolutionStrategy {
// 默认为版本优先模式
// failOnVersionConflict() // 切换策略为严格模式
// preferProjectModules() // 切换策略为项目优先模式。
}
}
总结
解析依赖图是一件复杂的事情。Gradle 在上面做的并不好。 或许在 5.0 上会有更稳定,更健壮的表现。所以我们需要特别关心最终打到 APK 的依赖版本。可以使用命令 gradlew :{moduleName}:dependencies 或参考 输出 Apk 所有的依赖 文章分析 APK 的依赖。
二 获取产物
缓存策略
在获取产物过程中,会先从缓存中获取。如果缓存有效。则直接从缓存中获取。缓存无效则请求远程仓库。
设置缓存策略:1
2
3
4
5
6
7
8configurations.all {
resolutionStrategy {
// cache dynamic versions for 10 minutes
cacheDynamicVersionsFor 10*60, 'seconds'
// don't cache changing modules at all
cacheChangingModulesFor 0, 'seconds'
}
}
这里可以配置两种缓存规则,
第一种是生效在动态版本上,动态版本包括最新版本和区间版本。最新版本是版本号使用
latest.
开头。 区间版本包括使用+
和使用开闭区间[()]
限制。第二种是生效在可变版本上。可变版本指的用
-SNAPSHOT
结尾的版本。注意全部为大写
默认缓存时间是一天。
所有非动态且非可变版本的依赖的缓存不受缓存策略的影响,默认是一直有效的。
属性匹配
在获取产物的时候可能会遇到一个异常。
Could not resolve all files for configuration ':app:dim'.
> Could not resolve project :lib.
Required by:
project :app
> Cannot choose between the following configurations of project :test1:
- debugApiElements
- debugRuntimeElements
- releaseApiElements
- releaseRuntimeElements
这是在Gradle 3.0 以上会出现的问题。 原因在于依赖是可以存在多个变种。在没有属性值匹配的时候会找到多个的产物。Gradle 会抛出异常。因为 Gradle 也不知道应该返回哪一个变种。这个时候需要对 Configuration
加上属性,来筛选出唯一的变种。
Gradle 支持属性值的转换。通过定义一些 Transform
提供一些属性转换规则。在属性匹配不上的时候,尝试组合一条最短的 Transform
路径, 进行转换匹配。 筛选出唯一的产物。关于属性匹配和 Transform
相关知识。这里就不展开聊,可以查看相关链接 Gradle Transform 初探 了解更多细节。