可以看到好几种类型的 NotationConverter
(依赖转换器):
// ① DependencyStringNotationConverter、DependencyMapNotationConverter 针对下面这种: implementation(io.reactivex:rxandroid:1.2.1) { transitive = false } // ② DependencyFilesNotationConverter 针对下面这种: implementation fileTree(dir:'libs', include:['*.jar']) // ③ DependencyProjectNotationConverter 针对下面这种: implementation project(":applemodule") // ④ DependencyClasspathNotationConverter 针对claspath依赖的情况
所以就是利用各种类型的转换器,解析成各种不同的依赖,点开没个转换器,可以看到生成的依赖有这两种:SelfResolvingDependency
和 ProjectDependency
,打开前者:
注释写道:SelfResolvingDependency是独立于Repository,可以自解析的依赖。而后者则依赖于另一个项目:
那就来跟一跟吧~
3. ProjectDependency
跟下:DependencyProjectNotationConverter
跟下:DefaultProjectDependencyFactory → create()
instantiator.newInstance 用于实例化一个具有DSL特性的对象,此处返回了一个 ProjectDependency
实例,而另外一个create()方法,则传多了一个configuration名称。
关于依赖创建的过程先了解到这里,继续往下走涉及到artifacts的东西,后续章节在继续跟,先总结下:
- ① DependencyHandler没有实现implementation、api这类方法(插件实现),利用MethodMissing机制间接调用这些方法;
- ② 不同的依赖声明由不同的转换器进行转换,最后转换为SelfResolvingDependency和ProjectDependency两类依赖对象;
0x4、依赖冲突解决
来到实用解决问题环节,在模块化,或者依赖别人开源库的时候,依赖冲突问题总是避无可避~
① xxx Not Fount
- 编译期:一般就是没有依赖正确的库导致;
- 运行期:一般是使用了compileOnly导致某些库只在编译时依赖;
② Program type already present com.xxx.XXX
可以点击右侧的Gradle面板中的:Tasks → android → android dependencies 查看依赖树:
也可以执行下述命令将依赖树输出到特定文件中,方便检索:
./gradlew :app:dependencies > dependencies.txt # 还可以分情况查看,如: gradlew :module_base:dependencies --configuration api # 查看指定库的依赖情况 /gradlew :app:dependencyInsight –dependency 指定库 –configuration compile # 使用build scan分析依赖,生成HTML可读性和界面好康些~ ./gradlew build --scan
接着就是根据编译报错结果定位到出问题的类,然后在依赖树中找到对应冲突的包了,接着是各类处理冲突的方法决策了。
③ 排除 & 禁用依赖传递
打开上面的依赖树,会发现有些依赖标注了 *
号,表示这个依赖被忽略了。
这就涉及到了 传递依赖
,Gradle解析一个库时,会自动下载它的依赖库,以及此依赖库的依赖库(递归),然后再处理这些众多依赖库的版本匹配,就很容易出现依赖冲突的问题了。
一种简单的解决方法就是:引用依赖库时,排除某个引起冲突的依赖库,到他时不往下传递,如:
implementation("io.reactivex.rxjava2:rxandroid:2.1.1") { exclude(group = "io.reactivex.rxjava2", module = "rxjava") exclude(group = "io.reactivex.rxjava2") } // 全局配置排除 configurations { compile.exclude module: 'xxx' all*.exclude group:'xxx.xxx', module: 'XXX' }
另一种就是 禁用依赖传递
,示例如下:
implementation("io.reactivex.rxjava2:rxandroid:2.1.1") { transitive = false } // 全局禁用 configurations.all { transitive = false }
④ 强制使用当前版本
通过添加 isForce == true
强制使用特定版本,同一个模块多个版本都被force,以第一个为准:
dependencies { implementation("io.reactivex.rxjava2:rxjava:2.2.6") { isForce = true } implementation("io.reactivex.rxjava2:rxjava:2.2.10") { isForce = true } }
上述的最终版本为2.2.6,force只作用于当前模块,不同模块间force相互独立,还要注意:模块force的某个依赖版本低于主模块的版本低,会发生编译错误,所以尽量不要在lib里force某个依赖的版本。
当然硬要这样搞也可以,主项目里再force一下(高低版本都可),或者是用下述代码替换依赖版本:
configurations.all { resolutionStrategy.eachDependency { DependencyResolveDetails details -> def requested = details.requested if (requested.group == 'com.android.support') { if (!requested.name.startsWith("multidex")) { details.useVersion '27.1.1'//27.1.1为当前版本号 } } } }
目前了解到用过的解决方法就这些,有遗漏的欢迎在评论区补充,谢谢~
0x5、依赖规则
1. 多项目间共享依赖版本
新版Gradle支持 依赖和版本分开声明,以前我们在多项目中通过ext指定依赖库版本,然后子模块一个个依赖的方法可以用下述方式替代:
// 子项目version,专门用于管理版本,使用constraint共享版本 dependencies { constraints { implementation("io.reactivex.rxjava2:rxjava:2.2.0") } } // 其他子项目通过platform将子模块的所有依赖约束引入 dependencies { implementation(platform(project(":version"))) // No version needed implementation("io.reactivex.rxjava2:rxjava") } // platform → require力度,想引入strictly力度,可以使用enforcedPlatform
2. 依赖约束力度
required
→ 我要依赖某个版本,但如果别的依赖提供了更高的版本则优先用更高的版本,默认。
strictly
→ 严格限制某个版本,常用于依赖版本降级,示例如下:
dependencies { implementation("io.reactivex.rxjava2:rxandroid:2.1.1") implementation("io.reactivex.rxjava2:rxjava:2.2.0!!") }
虽然rxandroid:2.1.1间接依赖了 rxjava:2.2.6, 但是由于 rxjava:2.2.0!! 严格约束的存在,最终决议为 rxjava:2.2.0,另外还支持范围区间约束,如[2.0.0, 2.2.0]!!,还有一点strictly相比起isForce无法降级直接依赖的组件版本。
prefer
→ 当没有更强的约束,则优先使用某个版本;
reject
→ 一般用于排除某个版本,比如有特定bug的版本;