前言

在上一篇中,主要讲解了Gradle核心模型以及初步了解Gradle插件。在这一篇中,主要对Gradle依赖管理进行详解:

1、依赖管理

所谓的依赖管理:在大多数情况下,项目都要依赖lib形式的可重用功能,还有很多项目可能被切分成多个单独的子工程来构成模块系统。依赖管理是一种可以让项目自动化的定义、解析,及使用依赖的技术。

而Gradle提供了强大的依赖管理支持,并且也实现了现代软件项目的各种典型场景,如下图所示:


如图所示

Gradle在编译构建的时候,先是从对应(本地、远程)资源库里面拿到对应的资源,所以这里就要对Gradle资源库进行详解:

2、Gradle资源库

在Gradle中存储模块的地方就叫做资源库:

  • 定义了资源库之后,Gradle就知道怎么样查找和检索模块。资源库有两种方式:本地库和远程库
  • 在运行时,如果相应的任务需要,那么Gradle就需要定位依赖的声明,依赖可能需要从远程库下载,也可能从本地库检索或者是在多项目构建中的其他项目。这个过程就叫做依赖解析
  • 一旦解析,解析机制就会存储依赖的底层文件作为本地缓存,之后的构建会重用这些文件,而不用再到远程库下载
  • 模块还能提供一些其他的元数据,元数据是描述模块更详细信息的数据,比如在资源库中的坐标,项目的信息等。(group, name, version)

刚刚说到,资源的下载是根据开发者对应Gradle定位依赖声明而下载的,那么Gradle依赖添加有哪些方式呢?

3、Gradle依赖仓库

添加依赖仓库:

  • 在项目build.gradle中使用allprojects{}对所有的工程进行配置,使用repositories{}添加依赖仓库;
  • google():添加一个在 Google 的 Maven 存储库中查找依赖项的存储库;
  • mavenCenteral():添加一个在 Maven 中央存储库中查找依赖项的存储库;
  • jcenter()添加一个在 Bintray 的 JCenter 存储库中查找依赖项的存储库;
    • 注意jecnter即将关闭,改用mavenCentral。如果有自己发布在jcenter的依赖库,需要迁移到mavenCentral。AS高版本中也默认的将jcenter()改成了mavenCenteral()。
  • mavenLocal():添加一个在本地 Maven 缓存中查找依赖项的存储库;
  • maven{}:指定某个maven仓库的地址,使用url(path)方法来添加;
  • ivy{}:指定某个ivy仓库地址,使用url(path)方法来添加。

说了那么多,贴个代码看看:

// 对所有的工程进行配置
allprojects {// 为工程添加依赖仓库repositories {google() // google maven仓库mavenCentral() // maven 中央仓库//jcenter() // Warning: this repository is going to shut down soonmavenLocal() // 本地maven仓库
//        maven {//            //url "http://xxxx" // 从xxx去获取library
//        }
//        ivy {//            //url "http://xxxx" // 从xxx去获取library
//        }}
}

相信大多数读者对这段代码熟悉都不能在熟悉了,也不过多叙述,直接进入下个专题:Gradle依赖范围

4、Gradle依赖范围

  • 在Gradle构建脚本中开发者可以把依赖定义到不同的范围中,比如编译源码或者执行测试。在Gradle中依赖的范围叫依赖项配置
  • Gradle插件内置了几种方式的依赖项配置:
    • implementation、api、compileOnly、runtimeOnly、annotationProcessor、lintChecks、lintPublish …
    • apk/compile/provided已被废弃。
  • 在build.gradle中使用dependencies{}添加依赖项配置。

4.1 Gradle不同依赖方式的作用

这里太多了,我就直接截图官方的了:感兴趣的可以去看看官方文档

这些都是官方原生依赖配置,当然你也可以通过自定义依赖:

4.2 Gradle自定义依赖配置项

app下bulid.gradle

...略
configurations {abc {//可在这实现依赖前后的逻辑println "abc"}
}// 依赖项配置
dependencies {abc "org.jetbrains.kotlin:kotlin-stdlib:1.5.10"...略
}

运行效果

Starting Gradle Daemon...
Gradle Daemon started in 2 s 539 ms> Configure project :app
abcBUILD SUCCESSFUL in 34m 9s

这个很少用,除特殊情况外几乎用不到,直接简单过一下。

直接到本篇重点,依赖传递:

5、Gradle依赖传递

依赖传递:就是通过不同的依赖方式有着不一样的传递关系。那么怎么来查看对应的依赖项?

5.1 查看模块依赖项

要想查看整个项目的依赖传递关系,使用命令:

  • gradlew app:dependencies --configuration releaseRuntimeClasspath
  • x.x.x (*) 该依赖已经有了,将不再重复依赖
  • x.x.x -> x.x.x 该依赖的版本被箭头所指的版本代替
  • x.x.x -> x.x.x(*) 该依赖的版本被箭头所指的版本代替,并且该依赖已经有了,不再重复依赖

现在来撸码验证下:

dependencies {abc "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"//implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"implementation 'androidx.core:core-ktx:1.3.1'//这种方式和下面那种相同,androidx.core:core-ktx:1.3.1 分为 // group: 'androidx.core', name: 'core-ktx', version: '1.3.1'implementation group: 'androidx.core', name: 'core-ktx', version: '1.3.1'implementation'androidx.appcompat:appcompat:1.2.0'implementation 'androidx.appcompat:appcompat:1.1.0'implementation 'androidx.appcompat:appcompat:1.+'...略}

注意这里,我特意将 androidx.appcompat:appcompat依赖了不同版本,其中1.+表示最新,执行以下上面那个命令看看效果:


+--- androidx.appcompat:appcompat:1.2.0 -> 1.4.0-rc01
|    +--- androidx.annotation:annotation:1.3.0-rc01
|    +--- androidx.core:core:1.7.0 (*)
|    +--- androidx.cursoradapter:cursoradapter:1.0.0
|    ....略
|    ....略
|         \--- androidx.annotation:annotation:1.1.0 -> 1.3.0-rc01
+--- androidx.appcompat:appcompat:1.1.0 -> 1.4.0-rc01 (*)
+--- androidx.appcompat:appcompat:1.+ -> 1.4.0-rc01 (*)

从这个运行效果可知:

  • Gradle依赖管理进行了优化

    • 如果项目存在同一个依赖库的多个版本,默认选择最高版本
    • Gradle会自动排除重复的依赖
    • Gradle默认支持依赖传递

既然Gradle依赖给我们做了优化,那为什么我们还是会遇到依赖冲突的问题?

  • 当引入一个第三方库,该库中也依赖了Android支持库,且支持库的版本和当前版本不一致。这种情况下,Gradle会自动选择最高的版本,导致不兼容的问题。
  • 如果同时依赖一个group下的全量库,又依赖了这个group其中一个分库,如果这两个库版本不统一,就导致了依赖冲突问题。最典型的就是support-v4的24.2.0版本开始的分库导致的问题
  • androidx包和support包的冲突问题

这个问题在之前经常出现,当你依赖A,A又依赖B,此时你依赖了C,而C没有依赖A而是直接依赖了B,当双方的B版本不同时,就会出现依赖冲突问题。

笔者用了以前最经典的support-v7、support-v4包,来举例,可惜的冲突问题复现不出来了,一看依赖树,gradle已经帮我们全转成androidx了。

+--- com.android.support:appcompat-v7:20.0.0 -> androidx.appcompat:appcompat:1.2.0 (*)
+--- com.android.support.constraint:constraint-layout:1.1.3 -> androidx.constraintlayout:constraintlayout:2.0.1 (*)
+--- com.android.support:support-v4:23.0.0 -> androidx.legacy:legacy-support-v4:1.0.0

虽然系统资源包gradle已经帮我们解决好了,但为了防止以后因为非系统资源包而出现的依赖冲突问题,仔细斟酌了下,还是将解决方案说一下,毕竟多会一点也不是坏事,还是要做到未雨绸缪。

5.2 解决依赖冲突的方式

5.2.1 排除传递依赖项

就以implementation ("androidx.room:room-runtime:2.3.0") 这个举例,我们通过依赖树看看这个依赖子下有哪些:

+--- androidx.room:room-runtime:2.3.0
|    +--- androidx.room:room-common:2.3.0
|    |    \--- androidx.annotation:annotation:1.1.0 -> 1.3.0-rc01
|    +--- androidx.sqlite:sqlite-framework:2.1.0
|    |    +--- androidx.annotation:annotation:1.0.0 -> 1.3.0-rc01
|    |    \--- androidx.sqlite:sqlite:2.1.0
|    |         \--- androidx.annotation:annotation:1.0.0 -> 1.3.0-rc01
|    +--- androidx.sqlite:sqlite:2.1.0 (*)
|    +--- androidx.arch.core:core-runtime:2.0.1 -> 2.1.0 (*)
|    \--- androidx.annotation:annotation-experimental:1.1.0

我们可以看到androidx.room:room-runtime 这个依赖树里面结构为:

  • androidx.room:room-common:2.3.0

    • androidx.annotation:annotation:1.1.0
  • androidx.sqlite:sqlite-framework:2.1.0
    • androidx.annotation:annotation:1.0.0
    • androidx.sqlite:sqlite:2.1.0
      • androidx.annotation:annotation:1.0.0
  • androidx.sqlite:sqlite:2.1.0 (*)
  • androidx.arch.core:core-runtime:2.0.1

通过这个结构能清楚的认识到,每一个依赖具体依赖了哪些子依赖,子依赖的下一层又依赖了哪些子依赖,甚至还会有多重子依赖。

这里我们看到不同的子依赖依赖了不同版本的annotation:annotation依赖,我们先假设annotation:annotation依赖并没有转成1.3.0-rc01,那么编译肯定会出现依赖冲突的问题,那么可以如下方式解决该问题。

    implementation ("androidx.room:room-runtime:2.3.0"){exclude group:'androidx.annotation', module: 'annotation'}
//    implementation group: 'androidx.annotation', name: 'annotation', version: '1.0.0' 等同于implementation("androidx.annotation:annotation:1.0.0")

在Groovy代码里面,额外创建了一个闭包,在闭包里面通过exclude关键字指定了要删除room-runtime里面对应的annotation依赖。随后在外部将annotation进行单独依赖,再次看看依赖树:

+--- androidx.room:room-runtime:2.3.0
|    +--- androidx.room:room-common:2.3.0
|    +--- androidx.sqlite:sqlite-framework:2.1.0
|    |    \--- androidx.sqlite:sqlite:2.1.0
|    +--- androidx.sqlite:sqlite:2.1.0
|    +--- androidx.arch.core:core-runtime:2.0.1 -> 2.1.0 (*)
|    \--- androidx.annotation:annotation-experimental:1.1.0
+--- androidx.annotation:annotation:1.0.0 -> 1.3.0-rc01

我们发现annotation:annotation有且仅有一个了。

那么如果出现多个依赖库都依赖了annotation:annotation 并且版本都不一样该怎么办?

5.2.2 Froce强制指定

// 强制指定版本
configurations.all {resolutionStrategy {force 'androidx.annotation:annotation:1.0.0'}
}
// 依赖项配置
dependencies {...略implementation ("androidx.room:room-runtime:2.3.0")//{//        exclude group:'androidx.annotation', module: 'annotation'
//    }
//    implementation group: 'androidx.annotation', name: 'annotation', version: '1.0.0' 等同于
//    implementation("androidx.annotation:annotation:1.0.0")
}

这里我们看到,在dependencies 使用了 force 强制将annotation:annotation 指定为 1.0.0,现在来看看效果:

+--- androidx.room:room-runtime:2.3.0
|    +--- androidx.room:room-common:2.3.0
|    |    \--- androidx.annotation:annotation:1.1.0 -> 1.0.0
|    +--- androidx.sqlite:sqlite-framework:2.1.0
|    |    +--- androidx.annotation:annotation:1.0.0
|    |    \--- androidx.sqlite:sqlite:2.1.0
|    |         \--- androidx.annotation:annotation:1.0.0
|    +--- androidx.sqlite:sqlite:2.1.0 (*)
|    +--- androidx.arch.core:core-runtime:2.0.1 -> 2.1.0 (*)
|    \--- androidx.annotation:annotation-experimental:1.1.0

我们通过这句代码annotation:annotation:1.1.0 -> 1.0.0 可以看到就算依赖了高版本的 1.1.0 也会被强制变成为 1.0.0。

6、总结

这一篇文字很多,但是内容比较少,相信读者看完本篇后还是会有所收获。

Android—Gradle教程(三)相关推荐

  1. Android Gradle(三)Groovy快速入门指南

    本文首发于微信公众号「刘望舒」 原文链接:Groovy快速入门看这篇就够了 前言 在前面我们学习了为什么现在要用Gradle?和Gradle入门前奏两篇文章,对Gradle也有了大概的了解,这篇文章我 ...

  2. android Gradle 教程

    发现一个官方翻译后的gradle教程:http://avatarqing.github.io/Gradle-Plugin-User-Guide-Chinese-Verision/index.html

  3. Android入门教程三十六之BaseAdapter优化

    上一节中我们学习了如何来使用一个ListView以及自定义一个简单的BaseAdapter,我们从代码 中可以看出比较重要的两个方法:getCount()和getView(),界面上有多少列就会调用多 ...

  4. Android初级教程三个Dialog对话框小案例

    这里把三个对话框形式写在一个项目程序里面,用三个按钮控制显示什么样式的对话框. 先看布局文件代码: <LinearLayout xmlns:android="http://schema ...

  5. Android入门教程三之使用Eclipse+ADT+SDK开发安卓APP

    前言: 1.这里我们有两条路可以选,直接使用封装好的用于开发Android的ADT Bundle,或者自己进行配置 因为谷歌已经放弃了ADT的更新,官网上也取消的下载链接,这里提供谷歌放弃更新前最新版 ...

  6. Android基础教程(三)之------ Activity 窗口切换

    首先在layout里建2个xml文件 分别有按钮1和按钮2 JAVA代码: 1.public class Activity01 extends Activity {  public void onCr ...

  7. Android学习教程三分钟学会安卓开发

    1.创建页面(activity_main是默认主页面) 可拖拽控件,点击下方text切换到代码 下图红框中的是控件的默认id 2.创建activity class(与layout一一对应) 在清单文件 ...

  8. Android开发实战三之导入现有Android工程项目

    选择Create project from existing sample 打开Eclipse环境后,在左上方的菜单上选择[File->New->Project],会弹出[New Proj ...

  9. android真实项目教程(三)——首页初点缀_by_CJJ

    大家晚上好,CJJ不好,前天打球,把右手弄脱臼了...搞得我现在只能一只手敲代码...那效率,我给自己跪了 ...写了好久,才写了那么一丁点...明明还有好多要说的...也只能等手好了再继续吧...呵 ...

  10. android groovy方法,Android Gradle从入门到精通(三)Groovy快速入门指南

    前言 在前面我们学习了为什么现在要用Gradle?和Gradle入门前奏两篇文章,对Gradle也有了大概的了解,这篇文章我们接着来学习Groovy的基础,要想学好Gradle,Groovy是必须要掌 ...

最新文章

  1. python实现洗牌算法_为什么渔民耶茨最有用的洗牌算法?
  2. 【c语言】数组逆序排列
  3. JQueryEasyUI学习笔记(十四)tree
  4. mysql show schema_快速入门 · xiaoboluo768/mysql-system-schema Wiki · GitHub
  5. Python IDLE入门
  6. 二叉树的直径—leetcode543
  7. 利用逆矩阵解线性方程组_机器人学导论---第四章 操作臂逆运动学(一)4.1-4.11...
  8. 最大连续和问题【四种不同的算法】
  9. upnp协议和dlna源码理解与修改
  10. C#网络编程示例(note)
  11. 计算机系统-电路设计09-计数器的内部电路实现
  12. bugku 杂项 流量分析(cnss)
  13. 团队管理35-管理方法论积累
  14. 使用阿里云邮件推送服务发送验证码
  15. Sobel边缘检测算子的本质
  16. 记一下怎样关闭windows defender安全中心
  17. 本题要求编写程序,先将输入的一系列整数中的最小值与第一个数交换,然后将最大值与最后一个数交换,最后输出交换后的序列
  18. Adobe PS常用快捷键
  19. POC原型开发:巡店 一期结果
  20. 关于在袁教授blog中的”呛袁教授“的争论

热门文章

  1. Syong :静态代理模式
  2. 基于stm32的自动调速风扇
  3. 压缩文件密码暴力破解——cRARk使用方法
  4. Topaz Adjust AI Mac
  5. 瑞星网络版防病毒软件For Linux通过银河麒麟产品兼容性测试
  6. Excel 数据透视表小技巧之 05 如何计算重复项,一个包含重复的邮政编码列表,需要每个邮政编码有多少个实例(教程含数据)
  7. 你的优势在哪?开宝马的学渣教会我的事
  8. c++第二课 输出自定义字符图形
  9. 编译 libstring.so lib库 libstring.c libstring.h makefile
  10. qq连连看java版_java仿QQ连连看游戏