Android Gradle的神奇之处 ---- Gradle依赖配置

Android依赖配置是Android开发过程中最常使用的,但是很多人只是会用,在dependencies中配置依赖项,但是并不知道,Gradle的依赖管理的原理

Gradle依赖配置

  • 1 Gradle依赖管理
  • 2 Gradle仓库配置
    • 2.1 buildscript / repositories
    • 2.2 allprojects
  • 3 Gradle依赖传递
    • 3.1 依赖重复
    • 3.2 依赖冲突

1 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")
}

2 Gradle仓库配置

# 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的作用则是不一样的

2.1 buildscript / repositories

在buildscript中的repositories是为了gradle的构建添加依赖,为classpath中的插件服务;那么classpath跟implementation有什么区别呢?

implementation:拉取library,并在打包的时候,将lib打包到apk中
classpath:只是为脚本提供library,例如Android的gradle插件,kotlin的gradle插件等,但是在打包的时候 并不会打包到apk中

2.2 allprojects

为所有的项目配置依赖仓库,之前都是在根project的build.gradle中配置,但现在都是在setting.gradle中的repositories中配置,但注意不要在两个地方都配置,会冲突

allprojects {
	google()
	mavenCenter()
	maven{
		url 'xxxx'
	}
}

其中几个仓库介绍下
google():google的maven仓库
mavenCenter():maven的中央仓库
mavenLocal:本地的依赖仓库
maven:远程maven仓库,url:仓库url地址

3 Gradle依赖传递

在一个模块中,如果使用implementation,那么这个依赖项只能在当前模块中使用;如果某个模块依赖了这个模块,想要使用这个依赖项,就可以使用api来传递依赖
Android Gradle的神奇之处 ---- Gradle依赖配置_第1张图片
注意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,那么每部依赖就会关闭,需要手动将每个依赖显式地引进来。通过这个方式也可以解决依赖冲突,后续会讲到

3.1 依赖重复

在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

3.2 依赖冲突

既然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的时候,也遇到过这个问题

你可能感兴趣的:(技术,android,gradle,开发语言,maven,依赖冲突)