一:依赖配置
目前 Gradle 版本支持的依赖配置有:implementation、api、compileOnly、runtimeOnly 和 annotationProcessor
1)implementation:会将依赖打包到输出(aar或apk),在编译时不会将依赖的实现暴露给其他module,也就是只有在运行时其他module才能访问这个依赖中的实现。
2)api :会将依赖打包到输出(aar 或apk),这个依赖可以传递,其他 module 无论在编译时和运行时都可以访问这个依赖的实现。
举个例子,A 依赖 B,B 依赖 C,如果都是使用 api 配置的话,A 可以直接使用 C 中的类(编译时和运行时)。而如果是使用 implementation 配置的话,在编译时,A 无法访问 C 中的类。
3)compileOnly :编译时使用,不会打包到输出(aar 或 apk)。这可以减少输出的体积,在只在编译时需要,在运行时可选的情况,很有用。
4)runtimeOnly :只在生成apk的时候参与打包,编译时不会参与,很少用
5)annotationProcessor :用于注解处理器的依赖配置。
依赖类型
1)本地源码依赖 : implementation project(“:mylibrary”)
2)本地libs目录jar包等依赖 : implementation fileTree(dir: ‘libs’, include: [‘*.jar’])
3)远程Maven仓库等依赖 : implementation ‘com.example.android:app-magic:12.3’
查看依赖关系
1)./gradlew (moudle 名称):dependencies
2) 通过as 提供的工具 :
依次选择:view > Tool Windows > Gradle
依次展开:appName > Tasks > android > androidDependencies 。gradle执行该任务后,系统会打印依赖关系
二:解决Gradle的依赖冲突
1)force 强制使用某个版本的库
force : force = true 强制使用某个版本。出现冲突时,优先使用该版本解决。
2)transitive 关闭依赖传递
transitive : 是否传递本身的依赖给宿主程序(使用传递依赖时,Gradle 会将传递依赖一起下载下来。 默认是开启传递依赖
transitive = true,依赖同于没有使用 exclude 排除依赖 ,每个包的依赖项都会被递归分析并添加进来。
transitive = false,则依赖关系同于用 exclude 排除依赖。
3)exclude 排除某项库
三:统一管理版本号
如果通过相同的方式引入不同版本的依赖库,默认会选择最新版本,不同的方式引入则会产生依赖冲突。
方案1
:在根目录下的build.gradle文件下添加 ext{ …. }
ext{
//dependencies
supportLibraryVersion ='26.1.0'
gsonVersion = '2.8.0'
}
方案2
:使用自定义gradle
第一步:我们在项目根目录下创建一个任意命名的xxx.gradle文件. 例如 : config.gradle
ext {
android = [
compileSdkVersion: 26,
buildToolsVersion: "25.0.0",
minSdkVersion : 14,
targetSdkVersion : 22,
versionCode : 17,
versionName : "1.7",
applicationId : "com.ml.xx",
]
dependencies = [
appcompatv7: "com.android.support:design:22.2.0",
loadtoast: "net.steamcrafted:load-toast:1.0.6",
constraintlayout: "com.android.support.constraint:constraint-layout:1.0.2"
]
}
第二步:在 根目录下的build.gradle于引用当前gradle:
apply from : “config.gradle”
第三步:在app下的build.gradle先定义出引用:
def cfg = rootProject.ext.android
def dpc = rootProject.ext.dependencies
完整代码:
def cfg = rootProject.ext.android
def dpc = rootProject.ext.dependencies
android {
compileSdkVersion cfg.compileSdkVersion
buildToolsVersion cfg.buildToolsVersion
...
}
四:dependencies 依赖分析
先来谈谈符号,它们的目的仅用于格式化:
+- – – : 是依赖分支库的开始。
| : 标识还是在之前的依赖库中的依赖,显示它依赖的库。
\- – – : 是依赖库的末尾。
* : 在依赖库的末尾,意味着该库的进一步依赖关系不会显示,因为它们已经列在其他某个子依赖树中。
-> : 如果 Gradle 发现多个依赖库都依赖到同一个库但是不同版本,那么它必须做出选择。毕竟包含同一个库的不同版本是没有意义的。在这种情况下,Gradle 默认选择该库的最新版本。
五:依赖冲突三类
在引用依赖时经常会有这样的问题:某些间接引用的依赖项是不需要的;产生了依赖冲突。此时需要排除一些依赖。
排除依赖的方式:
1)
在dependency中排除 : 这种方式是粒度最细的,也是最为繁琐的。此时可以考虑全局设置。
dependencies {
compile('com.malei:xxx:1.0') {
exclude module: 'cglib' //by artifact name
exclude group: 'org.jmock' //by group
exclude group: 'org.unwanted', module: 'iAmBuggy' //by both name and group
}
}
2)
在全局配置中排除
configurations {
compile.exclude module: 'cglib'
all*.exclude group:'org.unwanted', module: 'iAmBuggy'
}
禁用传递依赖方式:
1)
在dependency中禁用
compile('com.malei:xxx:1.0') {
transitive = false
}
2)
在全局配置中禁用
configurations.all {
transitive = false
}
3)
使用@jar : 在单个依赖项中使用@jar标识符忽略传递依赖
compile 'com.zhyea:ar4j:1.0@jar'
强制使用版本方式:
如果某个依赖项是必需的,而又存在依赖冲突时,此时没必要逐个进行排除,可以使用force属性标识需要进行依赖统一
1)在dependency强制使用版本
compile('com.malei:xxx:1.0') {
force = true
}
2)在全局配置中强制使用版本
configurations.all {
resolutionStrategy {
force 'org.hamcrest:hamcrest-core:1.3'
}
}
实践:
1)
implementation (‘com.alibaba:arouter-api:1.3.1’)
依赖关系:
\--- com.alibaba:arouter-api:1.3.1
+--- com.alibaba:arouter-annotation:1.0.4
\--- androidx.legacy:legacy-support-v4:1.0.0
+--- androidx.core:core:1.0.0 -> 1.3.1 (*)
| | \--- androidx.core:core:1.0.0 -> 1.3.1 (*)
| \--- androidx.cursoradapter:cursoradapter:1.0.0 (*)
\--- androidx.fragment:fragment:1.0.0 -> 1.1.0 (*)
2)
添加 exclude
implementation (‘com.alibaba:arouter-api:1.3.1’) {
exclude module : ‘fragment’
}
结论:我们发现androidx.fragment:fragment 包没有了
\--- com.alibaba:arouter-api:1.3.1
+--- com.alibaba:arouter-annotation:1.0.4
\--- androidx.legacy:legacy-support-v4:1.0.0
\--- androidx.legacy:legacy-support-core-ui:1.0.0
| \--- androidx.core:core:1.0.0 -> 1.3.1 (*)
\--- androidx.cursoradapter:cursoradapter:1.0.0 (*)、
3)
添加 transitive
transitive : 依赖传递特性,使用的时候,通常设置为 false 。即关闭依赖传递
例如项目中不希望 recyclerview 使用它所依赖的库 :
结论:transitive默认就是true
implementation ('com.alibaba:arouter-api:1.3.1') {
transitive = true
}
依赖图:
\--- com.alibaba:arouter-api:1.3.1
+--- com.alibaba:arouter-annotation:1.0.4
\--- androidx.legacy:legacy-support-v4:1.0.0
+--- androidx.core:core:1.0.0 -> 1.3.1 (*)
| | \--- androidx.core:core:1.0.0 -> 1.3.1 (*)
| \--- androidx.cursoradapter:cursoradapter:1.0.0 (*)
\--- androidx.fragment:fragment:1.0.0 -> 1.1.0 (*)
implementation ('com.alibaba:arouter-api:1.3.1') {
transitive = false //transitive为false表示单独依赖,true为默认树状依赖
}
依赖图
+--- project :router_core
\--- com.alibaba:arouter-api:1.3.1 //下面没有任何的依赖了
4)
force : 强制指定依赖版本
先看没有添加前的依赖图:
| | +--- androidx.fragment:fragment:1.3.4
| | | +--- androidx.annotation:annotation:1.1.0 -> 1.2.0
| | | +--- androidx.core:core:1.2.0 -> 1.5.0 (*)
| | | +--- androidx.collection:collection:1.1.0 (*)
| | | +--- androidx.viewpager:viewpager:1.0.0
| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.2.0
| | | | +--- androidx.core:core:1.0.0 -> 1.5.0 (*)
| | | | \--- androidx.customview:customview:1.0.0
| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.2.0
| | | | \--- androidx.core:core:1.0.0 -> 1.5.0 (*)
添加如下:
implementation (‘androidx.core:core:1.6.0’){
force=true
}
或者:
configurations.all {
resolutionStrategy {
force ‘androidx.core:core:1.6.0’
}
}
结论:所有的都变成了1.6.0版本了
查看依赖图:
| | +--- androidx.fragment:fragment:1.3.4
| | | +--- androidx.core:core:1.2.0 -> 1.6.0 (*)
| | | +--- androidx.viewpager:viewpager:1.0.0
| | | | +--- androidx.core:core:1.0.0 -> 1.6.0 (*)
| | | | \--- androidx.customview:customview:1.0.0
| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.2.0
| | | | \--- androidx.core:core:1.0.0 -> 1.6.0 (*)
| | | +--- androidx.loader:loader:1.0.0
| | | | +--- androidx.annotation:annotation:1.0.0 -> 1.2.0
| | | | +--- androidx.core:core:1.0.0 -> 1.6.0 (*)
5)
删除项目里所有的 androidx.core:core 依赖
configurations {
all*.exclude group: ‘androidx.core’, module: ‘core’
}
| | +--- androidx.fragment:fragment:1.3.4
| | | +--- androidx.annotation:annotation:1.1.0 -> 1.2.0
| | | +--- androidx.collection:collection:1.1.0 (*)
问题:
1)项目中多个模块依赖同一个依赖 D 的不同版本,如何保持每个模块直接使用自己本身 compile 的依赖版本呢?
app moduel 中的gradle依赖:
implementation ‘com.squareup.okhttp3:okhttp:3.6.0’
bbs module 中的gradle依赖:
implementation ‘com.squareup.okhttp3:okhttp:3.14.7’
然后我们依次看依赖情况:
MacBook-Pro:ComicFlutter malei$ ./gradlew :app:dependencies –configuration releaseCompileClasspath
> Task :app:dependencies
————————————————————
Project :app
————————————————————
+— com.squareup.okhttp3:okhttp:3.6.0 -> 3.14.7 //我们发现版本号已经被修改了
| \— com.squareup.okio:okio:1.17.2
+— androidx.appcompat:appcompat:{strictly 1.3.0} -> 1.3.0 (c)
MacBook-Pro:ComicFlutter malei$ ./gradlew bbs:dependencies –configuration releaseCompileClasspath
> Task :app:dependencies
————————————————————
Project :bbs
————————————————————
+— com.squareup.okhttp3:okhttp:3.14.7
| \— com.squareup.okio:okio:1.17.2
+— androidx.appcompat:appcompat:{strictly 1.3.0} -> 1.3.0 (c)
通过依赖树,我们发现 okhttp的依赖版本都变成了3.14.7 。 现在我们想要保持每个模块直接使用自己本身 compile 的依赖版本,该如何处理?
app.gradle中添加:
configurations.all {
// 遍历所有的依赖,根据 moduleName 使用对应的版本。确实可行
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
def requested = details.requested
System.out.println("MALEI: 打印 group : " + requested.group);
System.out.println("MALEI: 打印 name : " + requested.name);
if (requested.group == 'com.squareup.okhttp3') {
if (requested.name.startsWith("app")) {
details.useVersion '3.6.0'
} else if (requested.name.startsWith("bbs")) {
details.useVersion '3.14.7'
} else {
details.useVersion '3.6.0'
}
}
}
}
然后我们在看下结果:
MacBook-Pro:ComicFlutter malei$ ./gradlew :app:dependencies –configuration releaseCompileClasspath
————————————————————
Project :app
————————————————————
+— com.squareup.okhttp3:okhttp:3.6.0 //版本号保持了
| \— com.squareup.okio:okio:1.11.0
MacBook-Pro:ComicFlutter malei$ ./gradlew :bbs:dependencies –configuration releaseCompileClasspath
————————————————————
Project :bbs
————————————————————
+— com.squareup.okhttp3:okhttp:3.14.7
| \— com.squareup.okio:okio:1.17.2
2)本地 jar 依赖和 本地 aar 依赖区别。
jar 文件只包含编译好的 .class 文件和清单文件,不包含资源文件。所以如果没有 res 资源文件,可以在打包时,将 packaging 配置为 jar 格式;
aar 文件包含 class 以及 /res 目录下的所有资源文件。