Android Gradle依赖配置与依赖冲突解决
#.Gradle依赖配置
##.Gradle依赖管理与两个重要的classpath相关,每个Module都有:
1.编译时路径:compileClasspath
编译时能使用的代码,当一个类参与编译时,Gradle会将其放在compileClasspath中;编译过程会将源代码编译为.class文件。
2.运行时路径:runtimeClasspath
运行时使用的代码,当一个类参与打包时,Gradle就会将其放在runtimeClasspath中;运行时会将编译好的.class文件在JVM上运行。
各种不同依赖方式的关键区别就是:依赖树上的直接依赖包、间接依赖包的代码,是否允许加入编译时路径、运行时路径,或者是二者都允许加入。
Gradle3.0以后常用的关键字有implementation、api、compileOnly
1.implementation
与Gradle2.0的compile对应,会将直接依赖包同时添加到编译时路径、运行时路径,依赖包会被打入输出包中(aar或apk)。 差别在于对间接依赖包的处理上,使用implementation时直接依赖包不会向上层传递自己内部的依赖关系。
举例,A依赖B,B依赖C。对于A而言:B会同时加入A的编译时路径、运行时路径;但C不会加入A的编译时路径,只会加入A的运行时路径。因此,A在编译时只能访问B对外暴露的类和接口,不能访问C对外暴露的类和接口。因为B、C加入A的的运行时路径,所以对A打包时,B、C会打到A的输出包中。因为A不能直接访问C中的代码,所以修改C中代码时,只会影响直接依赖它的B,不会影响到更上层。所以重新对A打包时,只需要对C、B重新编译,不需要对A重新编译。
当有很多Module相互依赖,全部使用implementation时,修改一个Module,只有直接依赖它的Module受影响,绝大部分Module不需要重新编译,会显著提升构建时间。 一般建议配置依赖关系时尽可能使用implementation,而非api。
2.api
与Gradle2.0的compile对应,功能完全一样,会将直接依赖包和间接依赖包同时添加到编译时路径、运行时路径,并且会将依赖包依赖包会被打入输出包中(aar或apk)。 与 Gradle2.0的compile相同 , 编译时直接依赖包会向上层传递自己内部的依赖关系。
举例,A依赖B,B依赖C。对于A而言:B、C会同时加入A的编译时路径、运行时路径。因此,A在编译时既能访问B对外暴露的类和接口,也能访问C对外暴露的类和接口。因为B、C加入A的的运行时路径,所以对A打包时,B、C会打到A的输出包中。A能直接访问C中的代码,在编写代码调用时是方便了一些,但代价是修改C中对外暴露的代码时,不仅B受影响,A也受影响。所以重新对A打包时,C、B、A都需要重新编译。
当有很多Module相互依赖,全部使用api时,修改一个Module对外暴露的代码时,所有直接依赖它和间接依赖它的Module都受影响。如果修改的是依赖树较低层位置的Module,可能绝大部分Module后继都需要重新编译,构建时间会大大增加。 一般建议配置依赖关系时尽可能使用implementation,而非api。
3.compileOnly
与Gradle2.0的provided对应,依赖包只添加到编译时路径,不会添加到运行时路径,因此编译时可用,但不会打包到输出包中(aar或apk)。这可以减少输出包的体积,在只有编译时需要,运行时可选的情况下,很有用。例如可通过网络动态下载所需依赖包时,依赖包事先可不必打包到输出包里,当判断需要用到相关模块时,可以再下载。
4.runtimeOnly
与apk对应,依赖包不添加到编译时路径,只会添加到运行时路径,因此编译时不可用,但最终会打包到输出包中(aar或apk)。(没用过)
Gradle新老版本关键字对应一览表:
Gradle3.x+版本配置 |
已弃用配置(2.0及以前) |
api |
compile |
implement |
compile |
compileOnly |
provided |
runtimeOnly |
apk |
testImplementation |
testCompile |
androidTestImplementation |
androidTestCompile |
debugImplementation |
debugCompile |
releaseImplementation |
releaseCompile |
#.Gradle依赖冲突的出现与解决
1.依赖冲突出现的原因:
Android项目中有多个有相互依赖关系的Module,每个Module都依赖多个库(既可以是第三方库,也可以是Module打包成的aar等)。
Gradle会按照配置的依赖关系按照树形结构来做依赖解析,当发现依赖树中出现了同一个库的不同版本(意味着同一个库的不同实现代码),就会出现依赖冲突。
(默认情况下Gradle会尝试帮我们解决依赖冲突,解决的方式是使用最新的版本;这里的最新不是判断版本号大小,应该是根据发布时间来决定!没细究过其具体解决依赖冲突的方式。)
例:考虑如下场景:A 模块依赖 B1 和 C, D 模块依赖了 B2,其中 B1 和 B1 是同一个Module B的两个不同版本;同时,工程中我们同时依赖了 A 和 D。这种情况下,会存在针对Module B的依赖冲突!
2.依赖冲突的解决
2.1最好的方式,当然是依赖库不出现版本冲突。
可以自己写一个gradle脚本,例如上文中的utils.gralde,统一管理项目中用到的所有库的版本。
所有用到这些库的地方,统一都使用脚本中定义好的版本。
2.2通过transitive、exclude等关键字手动配置,解决冲突
1)transitive关键字:
对某个依赖库设置transitive关键字后,Gradle在解析依赖树时不会对该库内部的依赖关系做解析。
2)exclude关键字:
exclude关键字可以解除对依赖库内部的指定部分库的依赖解析。
3)force关键字:
force关键字可以强制设置指定Module依赖某个库。
示例,针对上面的依赖冲突示例,避免B1与B2的冲突:
(1) 方案一:针对 A 或 D 配置 transitive。
这里针对A配置,不解析A模块的传递依赖,因此当前Module的依赖关系树中不再包含 B1 和 C,这里需要手动添加依赖 C
dependencies {
implementation A {
transitive = false
}
implementation C
implementation D {
//transitive = false
}
}
(2) 方案二:针对 A 或 D 配置 exclude规则,此处针对A配置,依赖关系树中不再包含B1
dependencies {
implementation A {
exclude B1
}
implementation D {
//exclude B2
}
}
(3) 方案三:使用force强制依赖某个版本,如强制依赖 B1 或者 B2
以下是在顶层build.gradle中配置,强制所有module依赖B1
configurations.all {
resolutionStrategy {
force B1
// force B2
}
}
##.个人推荐的依赖配置方式
可以自己写一个gradle脚本,例如上文中的utils.gralde,统一管理项目中用到的所有第三方库的版本。
所有用到第三方库的地方,统一都使用脚本中定义好的版本。
至于自己写的Module,使用implementation方式,只对它直接用到的Module设置依赖关系(不要设置transitive=false,否则要把这些直接依赖包内部的包也统统设置到当前Module)。
这些可以尽可能少的配置依赖关系,而且只要直接依赖包的对外类、接口不变,无论依赖树上子孙节点内部如何修改,都不用重新编译当前包。修改代码时,需要重新编译的包较少,提高编译效率。
Android Gradle依赖配置与依赖冲突解决相关推荐
- maven依赖管理(依赖配置、依赖传递、依赖冲突、依赖范围)
文章目录 基本说明 依赖配置 依赖传递 直接依赖 间接依赖 依赖冲突 路径优先 声明优先 特殊依赖 可选依赖 排除依赖 可选依赖和排除依赖区别 依赖范围 总结 基本说明 这篇文章会介绍在maven中的 ...
- Maven的传递性依赖及其jar包冲突解决
一.Maven简介 Maven是一个跨平台的项目管理工具.作为Apache组织的一个颇为成功的开源项目,其主要服务于基于Java平台的项目创建,依赖管理和项目信息管理. 二.Maven的依赖管理 1. ...
- Android Gradle进阶配置指南
简单的总结一下gradle: 1.Gradle是一种构建工具,它可以帮你管理项目中的差异,依赖,编译,打包,部署......你可以定义满足自己需要的构建逻辑,写入到build.gradle中供日后复用 ...
- Android Gradle进阶配置指南 1
简单的总结一下gradle: 1.Gradle是一种构建工具,它可以帮你管理项目中的差异,依赖,编译,打包,部署......你可以定义满足自己需要的构建逻辑,写入到build.gradle中供日后复用 ...
- maven工程导入项目打开404_Maven依赖配置和依赖范围
教程前面用坐标一一对应地描述了构件,并且保存在仓库中了.那用坐标描述好后,把它们放在仓库中的作用是什么呢?当其他项目需要在这些构件的基础上做开发的时候,用户就没必要自己再重新实现一遍了.直接指定坐标, ...
- Maven依赖配置和依赖范围
文章目录 1.美图 2.参考 3.概述 4.依赖的配置 5.依赖的范围 5.1 compile 5.2 test 5.3 provided 5.4 runtime 5.5 system 5.6 imp ...
- Android Gradle 的配置从 Groovy 迁移到 KTS
目录 #1. 引用 #2.常用术语 #3.脚本文件命名 #4.Gradle迁移示例 1.准备工作:对 Groovy 做简单转换 2. plugins 代码块迁移 3. 显式和隐式 buildTypes ...
- Maven依赖配置、依赖传递和依赖范围。
一.依赖传递. 依赖具有传递性: 1.直接依赖:在当前项目中通过依赖配置建立的依赖关系. 2.间接依赖:被资源的资源如果依赖其他资源,当前项目简介依赖其他资源. 3.依赖传递冲突问题: 路径优先:当依 ...
- 交换机配置初始IP冲突解决方法
Primary IP address conflict 的解决方法: 举个例子:当掩码设置为16位时,我将3/1口的IP地址设置为192.16.20.1/16,3/2口的IP地址设置为192.16.3 ...
最新文章
- mysql noinstall_windows mysql noinstall
- IT项目之旅(二)篮球计分器(分析、设计、实现)
- 刘志勇:微博短视频百万级高并发架构
- chart控件做实时曲线显示_Python 如何实时绘制数据
- 【操作系统】使用循环创建线程,一个手残导致的bug
- CruiseControl.NET与TFS结合的配置文件
- java 程序流程控制知识点_JAVA基础知识点梳理三:流程控制语句
- splObjectStroge的作用,实例化一个数组
- 信度和效度经典例子_浅析经典目标检测评价指标--mmAP(一)
- 情怀再次输给现实!中国式星巴克,如今亏到连租金都交不起
- 浅谈css中一个元素如何在其父元素居中显示
- Life is not fair, get used to it.
- 电力系统matlab实验报告,电力系统分析潮流实验报告
- 深度解析,马斯克最新发射的先进火箭
- Image Tampering Detection via Semantic Segmentation Network
- 租车还能这么玩?快来看看神州租车
- 移动架构之MVP框架
- 如何替换 JavaScript 中所有出现的字符串
- 现代科技沃土滋养下的数据黑市和网络犯罪
- Java 三大器之监听器Listener