Android Gradle依赖配置与依赖冲突解决

#.Gradle依赖配置
##.Gradle依赖管理与两个重要的classpath相关,每个Module都有:
1.编译时路径:compileClasspath

    编译时能使用的代码,当一个类参与编译时,Gradle会将其放在compileClasspath中;
    编译过程会将源代码编译为.class文件。

2.运行时路径:runtimeClasspath

    运行时使用的代码,当一个类参与打包时,Gradle就会将其放在runtimeClasspath中;
    运行时会将编译好的.class文件在JVM上运行。

         各种不同依赖方式的关键区别就是:依赖树上的直接依赖包、间接依赖包的代码,是否允许加入编译时路径、运行时路径,或者是二者都允许加入。

Gradle3.0以后常用的关键字有implementation、api、compileOnly
1.implementation

        与Gradle2.0的compile对应,会将直接依赖包同时添加到编译时路径、运行时路径,依赖包会被打入输出包中(aar或apk)。 差别在于对间接依赖包的处理上,使用implementation时直接依赖包不会向上层传递自己内部的依赖关系。   

        举例,A依赖B,B依赖C。
        对于A而言:B会同时加入A的编译时路径、运行时路径;但C不会加入A的编译时路径,只会加入A的运行时路径。
        因此,A在编译时只能访问B对外暴露的类和接口,不能访问C对外暴露的类和接口。因为B、C加入A的的运行时路径,所以对A打包时,B、C会打到A的输出包中。
        因为A不能直接访问C中的代码,所以修改C中代码时,只会影响直接依赖它的B,不会影响到更上层。所以重新对A打包时,只需要对C、B重新编译,不需要对A重新编译。

        当有很多Module相互依赖,全部使用implementation时,修改一个Module,只有直接依赖它的Module受影响,绝大部分Module不需要重新编译,会显著提升构建时间。 一般建议配置依赖关系时尽可能使用implementation,而非api。

2.api

        与Gradle2.0的compile对应,功能完全一样,会将直接依赖包和间接依赖包同时添加到编译时路径、运行时路径,并且会将依赖包依赖包会被打入输出包中(aar或apk)。 与 Gradle2.0的compile相同 , 编译时直接依赖包会向上层传递自己内部的依赖关系。

        举例,A依赖B,B依赖C。
        对于A而言:B、C会同时加入A的编译时路径、运行时路径。
        因此,A在编译时既能访问B对外暴露的类和接口,也能访问C对外暴露的类和接口。因为B、C加入A的的运行时路径,所以对A打包时,B、C会打到A的输出包中。
        A能直接访问C中的代码,在编写代码调用时是方便了一些,但代价是修改C中对外暴露的代码时,不仅B受影响,A也受影响。所以重新对A打包时,C、B、A都需要重新编译。

        当有很多Module相互依赖,全部使用api时,修改一个Module对外暴露的代码时,所有直接依赖它和间接依赖它的Module都受影响。如果修改的是依赖树较低层位置的Module,可能绝大部分Module后继都需要重新编译,构建时间会大大增加。 一般建议配置依赖关系时尽可能使用implementation,而非api。

3.compileOnly

        与Gradle2.0的provided对应,依赖包只添加到编译时路径,不会添加到运行时路径,因此编译时可用,但不会打包到输出包中(aar或apk)。
        这可以减少输出包的体积,在只有编译时需要,运行时可选的情况下,很有用。例如可通过网络动态下载所需依赖包时,依赖包事先可不必打包到输出包里,当判断需要用到相关模块时,可以再下载。

4.runtimeOnly

        与apk对应,依赖包不添加到编译时路径,只会添加到运行时路径,因此编译时不可用,但最终会打包到输出包中(aar或apk)。(没用过)

 

#.Gradle依赖冲突的出现与解决


1.依赖冲突出现的原因:

        Android项目中有多个有相互依赖关系的Module,每个Module都依赖多个库(既可以是第三方库,也可以是Module打包成的aar等)。

        Gradle会按照配置的依赖关系按照树形结构来做依赖解析,当发现依赖树中出现了同一个库的不同版本(意味着同一个库的不同实现代码),就会出现依赖冲突。

        (默认情况下Gradle会尝试帮我们解决依赖冲突,解决的方式是使用最新的版本;这里的最新不是判断版本号大小,应该是根据发布时间来决定!没细究过其具体解决依赖冲突的方式。)

        例:考虑如下场景:A 模块依赖 B1 和 C, D 模块依赖了 B2,其中 B1 和 B1 是同一个Module B的两个不同版本;同时,工程中我们同时依赖了 A 和 D。这种情况下,会存在针对Module B的依赖冲突!

2.依赖冲突的解决
2.1最好的方式,当然是依赖库不出现版本冲突。

        可以自己写一个gradle脚本,例如上文中的utils.gralde,统一管理项目中用到的所有库的版本。

    所有用到这些库的地方,统一都使用脚本中定义好的版本。

2.2通过transitive、exclude等关键字手动配置,解决冲突

         1)transitive关键字:

                对某个依赖库设置transitive关键字后,Gradle在解析依赖树时不会对该库内部的依赖关系做解析。

        2)exclude关键字:    

                exclude关键字可以解除对依赖库内部的指定部分库的依赖解析。

        3)force关键字:    

                force关键字可以强制设置指定Module依赖某个库。

    示例,针对上面的依赖冲突示例,避免B1与B2的冲突:

    (1) 方案一:针对 A 或 D 配置 transitive。

        这里针对A配置,不解析A模块的传递依赖,因此当前Module的依赖关系树中不再包含 B1 和 C,这里需要手动添加依赖 C

    dependencies {

        implementation A {

          transitive = false

        }

        implementation C

        implementation D {

          //transitive = false

        }

    }

    (2) 方案二:针对 A 或 D 配置 exclude规则,此处针对A配置,依赖关系树中不再包含B1

    dependencies {

        implementation A {

          exclude  B1

        }

        implementation D {

          //exclude  B2

        }

    }

    (3) 方案三:使用force强制依赖某个版本,如强制依赖 B1 或者 B2

            以下是在顶层build.gradle中配置,强制所有module依赖B1

    configurations.all {

        resolutionStrategy {

           force B1

           // force B2

        }

    }

##.个人推荐的依赖配置方式

        可以自己写一个gradle脚本,例如上文中的utils.gralde,统一管理项目中用到的所有第三方库的版本。

    所有用到第三方库的地方,统一都使用脚本中定义好的版本。

        至于自己写的Module,使用implementation方式,只对它直接用到的Module设置依赖关系(不要设置transitive=false,否则要把这些直接依赖包内部的包也统统设置到当前Module)。

        这些可以尽可能少的配置依赖关系,而且只要直接依赖包的对外类、接口不变,无论依赖树上子孙节点内部如何修改,都不用重新编译当前包。修改代码时,需要重新编译的包较少,提高编译效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android GradleAndroid 开发中使用的构建工具,用于管理项目中的依赖库和构建过程。在 Android Gradle 中,可以通过在 build.gradle 文件中声明依赖来管理项目中所需的第三方库。 首先,要避免重复依赖,可以通过以下几种方式来解决: 1. 使用 implementation 替代 compile: 在构建文件中,使用 implementation 来声明依赖而不是 compile。implementation 会自动解决版本冲突,并排除掉传递性依赖中的冗余依赖。 2. 使用 exclude 指令: 如果遇到了冗余的依赖,可以使用 exclude 指令来排除掉特定的依赖项。在依赖声明中添加 exclude 指令,指定要排除的库名称和组织。 3. 使用统一的依赖版本: 可以在项目的 root build.gradle 文件中定义一个变量来统一管理依赖的版本,然后在所有的 module 的 build.gradle 文件中引用这个变量。 另外,如果希望忽略某个依赖,可以使用以下方式: 1. 使用 implementation 进行依赖声明: 如果在某个 module 的 build.gradle 文件中使用 implementation 来声明依赖,那么该依赖将不会传递给该 module 的依赖项。 2. 使用 exclude group 指令: 如果希望忽略某个库的所有依赖,可以在依赖声明中使用 exclude group 指令来排除掉某个组织的所有依赖。 总结来说,Android Gradle 提供了一些方法来管理和解决依赖冲突问题,包括使用 implementation 替代 compile,使用 exclude 指令去除重复依赖,使用统一的依赖版本,以及使用 implementation 和 exclude group 进行依赖的忽略。通过合理地使用这些方式,可以有效地管理依赖库并避免不必要的冲突和重复依赖

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值