Android依赖配置是Android开发过程中最常使用的,但是很多人只是会用,在dependencies中配置依赖项,但是并不知道,Gradle的依赖管理的原理
gradle依赖库的来源有2种:本地库和远程库
本地库主要用来做本地依赖,像在本地创建的module,可以通过依赖配置被其他模块使用
implementation project(path:'xxxsdk')
远程仓库通常需要依靠网络从仓库中拉取代码
implementation 'com.github:moduleName:2.1.0'
当一次配置项目的时候,都需要从本地或者远程仓库下载,然后在本地缓存,这也是为什么第一次拿到这个项目的时候,会build好久;第二次build的时候,就直接从本地拿缓存,会很快。
像从远程仓库中拉取代码,例如下方,依赖项主要分为3部分
implementation 'com.github:moduleName:2.1.0'
com.github:group分组
moduleName:name模块的名称
2.1.0:version版本
需要三者不变,每次都会用本地的缓存,如果其中任意一项变了,例如升级了版本,那么就会重新从远程仓库拉取代码
在gradle中,dependencies是委托给DependencyHandler处理,每个implementation项,其实就是DependencyHandler通过add的方式添加进来的,implementation就是一个configurationName,然后这个名称是可以自定义的
dependencies {
// implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
add("implementation","androidx.constraintlayout:constraintlayout:2.1.3")
}
# rootProject/build.gradle
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.3"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
# setting.gradle
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
}
}
在一个gradle工程里,有两个repositories,但是这两个repositories的作用则是不一样的
在buildscript中的repositories是为了gradle的构建添加依赖,为classpath中的插件服务;那么classpath跟implementation有什么区别呢?
implementation:拉取library,并在打包的时候,将lib打包到apk中
classpath:只是为脚本提供library,例如Android的gradle插件,kotlin的gradle插件等,但是在打包的时候 并不会打包到apk中
为所有的项目配置依赖仓库,之前都是在根project的build.gradle中配置,但现在都是在setting.gradle中的repositories中配置,但注意不要在两个地方都配置,会冲突
allprojects {
google()
mavenCenter()
maven{
url 'xxxx'
}
}
其中几个仓库介绍下
google():google的maven仓库
mavenCenter():maven的中央仓库
mavenLocal:本地的依赖仓库
maven:远程maven仓库,url:仓库url地址
在一个模块中,如果使用implementation,那么这个依赖项只能在当前模块中使用;如果某个模块依赖了这个模块,想要使用这个依赖项,就可以使用api来传递依赖
注意api只能传递给上级模块,不能跨层级传递依赖;如果不使用api,在运行时,模块1还是可以通过反射拿到模块2中的依赖项
com.facebook.fresco:fresco:0.14.1
| +--- com.facebook.fresco:drawee:0.14.1
| | \--- com.facebook.fresco:fbcore:0.14.1
| +--- com.facebook.fresco:fbcore:0.14.1
| \--- com.facebook.fresco:imagepipeline:0.14.1
但是如果不想进行内部依赖传递,可将transitive设置为false;什么是内部依赖,像fresco:fresco中,其实依赖了drawee、fbcore、imagepipeline这些模块,但是在配置依赖的时候,却只用一行代码就把所有的依赖包都下载下来了
implementation (com.facebook.fresco:fresco:0.14.1){
transitive false
}
implementation 'com.facebook.fresco:drawee:0.14.1'
implementation 'com.facebook.fresco:fbcore:0.14.1'
implementation 'com.facebook.fresco:imagepipeline:0.14.1'
但是如果把transitive设置为false,那么每部依赖就会关闭,需要手动将每个依赖显式地引进来。通过这个方式也可以解决依赖冲突,后续会讲到
在Gradle的依赖项中,如果同时依赖一个lib的不同版本,最终使用的就是声明的最高版本,其他版本就会移除
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.appcompat:appcompat:1.2.1'
例如我依赖了appcompat的三个版本,通过命令 ./gradlew app:dependencies可以查看项目的依赖树
+--- androidx.appcompat:appcompat:1.3.1 -> 1.4.1 (*)
+--- androidx.appcompat:appcompat:1.2.1 -> 1.4.1 (*)
+--- com.google.android.material:material:1.5.0
| +--- androidx.annotation:annotation:1.2.0 -> 1.3.0
| +--- androidx.appcompat:appcompat:1.1.0 -> 1.4.1 (*)
| +--- androidx.cardview:cardview:1.0.0
| | \--- androidx.annotation:annotation:1.0.0 -> 1.3.0
| +--- androidx.coordinatorlayout:coordinatorlayout:1.1.0
| | +--- androidx.annotation:annotation:1.1.0 -> 1.3.0
| | +--- androidx.core:core:1.1.0 -> 1.7.0 (*)
+--- androidx.fragment:fragment:1.3.6
| | +--- androidx.annotation:annotation:1.1.0 -> 1.3.0
| | +--- androidx.core:core:1.2.0 -> 1.7.0 (*)
| | +--- androidx.collection:collection:1.1.0 (*)
| | +--- androidx.viewpager:viewpager:1.0.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.3.0
| | | +--- androidx.core:core:1.0.0 -> 1.7.0 (*)
| | | \--- androidx.customview:customview:1.0.0 -> 1.1.0
| | | +--- androidx.annotation:annotation:1.1.0 -> 1.3.0
| | | \--- androidx.core:core:1.3.0 -> 1.7.0 (*)
我们可以看到,声明的appcompat版本1.2.1和1.3.1最终都升级为了1.4.1
其中怎么看依赖树,有些规则需要说一下
com.google.android.material:material:1.5.0
规则1:代表唯一依赖,其他地方没有依赖这个库
androidx.appcompat:appcompat:1.3.1 -> 1.4.1 (*)
规则2:版本 -> 版本(*),代表还存在该库其他版本的依赖或者间接依赖,选择后面这个版本
androidx.collection:collection:1.1.0 (*)
规则3:版本(*),代表存在该库的其他版本依赖或者间接依赖,默认使用这个版本的库,一般代表最新版本或者最高版本
从依赖树中就可以看到,androidx.appcompat有很多依赖的版本,但是最终都是指向了最高版本1.4.1
既然gradle会移除低版本,使用高版本,可为什么还是会出现依赖冲突呢?依赖冲突的本质是什么?
依赖冲突的本质就是类冲突,在一个library中依赖了(moduleA,moduleB),然后在项目中又显式依赖了moduleB,那么就会产生依赖冲突
dependencies{
implementation 'libraryA'
implementation 'moduleB'
}
最常见的是之前使用support库的时候,现在使用的版本是support-v4库
com.android.support:support-v4:21.0.3 -> androidx.legacy:legacy-support-v4:1.0.0
| +--- androidx.core:core:1.0.0 -> 1.7.0 (*)
| +--- androidx.media:media:1.0.0
| | +--- androidx.annotation:annotation:1.0.0 -> 1.3.0
| | +--- androidx.core:core:1.0.0 -> 1.7.0 (*)
| | \--- androidx.versionedparcelable:versionedparcelable:1.0.0 -> 1.1.1 (*)
| +--- androidx.legacy:legacy-support-core-utils:1.0.0 (*)
| +--- androidx.legacy:legacy-support-core-ui:1.0.0
| | +--- androidx.annotation:annotation:1.0.0 -> 1.3.0
| | +--- androidx.core:core:1.0.0 -> 1.7.0 (*)
| | +--- androidx.legacy:legacy-support-core-utils:1.0.0 (*)
| | +--- androidx.customview:customview:1.0.0 -> 1.1.0 (*)
| | +--- androidx.viewpager:viewpager:1.0.0 (*)
| | +--- androidx.coordinatorlayout:coordinatorlayout:1.0.0 -> 1.1.0 (*)
| | +--- androidx.drawerlayout:drawerlayout:1.0.0 -> 1.1.1 (*)
| | +--- androidx.slidingpanelayout:slidingpanelayout:1.0.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.3.0
| | | +--- androidx.core:core:1.0.0 -> 1.7.0 (*)
| | | \--- androidx.customview:customview:1.0.0 -> 1.1.0 (*)
| | +--- androidx.interpolator:interpolator:1.0.0 (*)
| | +--- androidx.swiperefreshlayout:swiperefreshlayout:1.0.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.3.0
| | | +--- androidx.core:core:1.0.0 -> 1.7.0 (*)
| | | \--- androidx.interpolator:interpolator:1.0.0 (*)
| | +--- androidx.asynclayoutinflater:asynclayoutinflater:1.0.0
| | | +--- androidx.annotation:annotation:1.0.0 -> 1.3.0
| | | \--- androidx.core:core:1.0.0 -> 1.7.0 (*)
| | \--- androidx.cursoradapter:cursoradapter:1.0.0 (*)
| \--- androidx.fragment:fragment:1.0.0 -> 1.3.6 (*)
可以看到v4包是分了很多模块,例如utils、ui等
implementation 'com.android.support:support-v7:21.0.3'
那么在v7包中,除了这些模块之外,v4包也依赖进去了,那势必就会造成依赖冲突
如果想要解决依赖冲突,可以使用exclude排除某个依赖项,这样在依赖中就不会存在这个模块的代码
implementation ('com.android.support:support-v7:21.0.3'){
exclude group:'com.android.support',module:'support-v4'
}
之前在项目中使用某个三方SDK的时候,也遇到过这个问题