关于 Gradle 依赖库的几个东西

3,994 阅读5分钟

一、 Gradle 的依赖包

在使用 eclipse 做项目的使用,如果要使用别人的库,要么clone工程到自己项目再添加依赖,或者导出为jar包引入,或者直接去找jar再添加到项目路径(add to path)。而使用maven,将查找下载jar这个过程简化,直接通过 dependency 指定依赖库名、版本,接着自动从maven下载并添加到项目中。

二、 Gradle 除了引用线上仓库版本,还可以使用本地模块依赖,离线 aar 和 jar 库,请问 aar 和 jar 有什么区别?

jar 只有 class 文件和清单文件,不包含资源文件,如图片 aar 包含所有资源,class 以及 res 资源文件,如图片,strings.xml 等

使用:

  1. jar 文件复制到 libs 目录,eclipse 直接导入右键 add to path,AndroidStudio 添加依赖如下
dependencies{//选择一个即可
	//添加 libs 目录下所有 jar
	implementation fileTree(include: ['*.jar'], dir: 'libs')

	//添加直接指定 jar
	implementation files('libs/xx.jar')
}
  1. 使用 aar,分两种,本地及远程仓库
//远程仓库
dependencies{
	implementation 'com.android.support:appcompat-v7:28.0.1'
}
//本地 aar,将aar放在项目目录中,如在libs,在module的 build.gradle中添加
repositories{//添加本地仓库
	flatDir{//指定仓库路径
		dirs 'libs'
	}
}
dependencies{
	implementation (name:'xxx',ext:'aar')
}

三、 Gradle 项目中,使用远程仓库,依赖库 A 引用库 B:1.0.0,项目依赖 B:1.2.0 ,是否会发生冲突,最终打包依赖 B 版本是哪个?如果A引用B:3.0.0呢?

分三种情况:

  1. A 依赖 B 时,使用jar形式,将 B 源码打包进 A 编译时没有问题,打包提示资源重复,打包失败
  2. A 依赖 B 时,使用jar形式,但没有将 B 源码打包进 A 没有问题,直接使用项目中依赖指定的版本,与 A 引用的 B 版本无关,可以说库 A 中的 B 不存在
  3. A 依赖 B 时,使用远程仓库的依赖 没有问题,出现版本号不一致,均使用最高版本号作为最终版本

以 Retrofit 和 OkHttp 为例,看看远程仓库依赖

注:Retrofit:2.5.0,OkHttp:3.12.0,okio:1.15.0

  1. 只引入 Retrofit 同步后项目截图:

    单引用截图

    简单测试一下接口,可用

    手机截图

  2. 手动引入 OkHttp:3.12.1 高版本,会点击同步

    引用高版本截图

  3. 降低版本试试,同步一下

    引用低版本截图

    这里,Gradle 对比引用依赖库版本,使用最高的版本作为项目使用的最终版本。 指定使用依赖库低版本时,在 gradle 中提示 “A newer version of com.squareup.okhttp3:okhttp than 3.10.0 is available: 3.14.0” 更多信息

This detector looks for usages of libraries where the version you are using is not the current stable release. Using older versions is fine, and there are cases where you deliberately want to stick with an older version. However, you may simply not be aware that a more recent version is available, and that is what this lint check helps find. 意思是有更高的版本,但是你要使用低版本也没有问题,他仅仅是编译器检查告知我们有更新的版本可以使用,最后使用的也是高版本。

  1. 如果,我非要使用我指定的低版本使用,该怎么写依赖? 使用exclude去除版本依赖,代码如下:
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    
    implementation('com.squareup.retrofit2:retrofit:2.5.0') {
        exclude (group: 'com.squareup.okhttp3' ,module: 'okhttp')
    }
    implementation 'com.squareup.okhttp3:okhttp:3.10.0'
}

同步后,项目

强制引用截图

结论: 从上面也可以看出来,远程依赖,一般不会出现重复依赖的问题,因为在比对版本后,会统一使用更高版本,这里也引出一个知识点,就是远程依赖其实没有把依赖的库的代码打包在一起,各自彼此独立,只是我要引用到你的库,才会去仓库把你下载下来,依赖可以理解为名义上的关系。

四、依赖库重复打包失败举例,上面第一种情况举例,以 Retrofit:2.5.0 和 cn.bmob.android:bmob-sdk:3.5.1

有时也会发现,我们依赖库,会出现库冲突,也就是

org.gradle.api.tasks.TaskExecutionException: Execution failed for task':app:transformDexArchiveWithExternalLibsDexMergerForRelease'.

具体如下:

首先编译安全通过:

编译项目截图

但是,打包失败!!

编译项目截图

双击 Run Tasks 查看异常,提示合并失败,我们查看引用库,发现 bmob 是将 OkHttp、okio 以jar包形式引入,即 bmob库拥有 OkHttp、okio 源码,我们项目的 Retrofit 引用 OkHttp、okio 又下载了这两个库源码,所以存在两个不同版本的 okhttp、okio,所以打包重复资源合并失败。 解决方案,移除任何一方的库,使用 exclude,但需注意的是,如果源码以jar包形式存在则无法使用 exclude 去除,只能移除远程仓库依赖部分。

解决示例

五、编写库,注意依赖打包问题

所以在编写库的时候,一般依赖其他库时,能用远程库最好,不能的话使用jar时,如果不是特别难找的包,可以使用compileOnly(老版本provided)声明jar包只用于编译阶段,打包不将jar打包进自己的库。

三种不同依赖,打包后具体如下:

  1. 使用远程仓库依赖,生成aar不会打包引用的库源码

    远程

  2. 使用jar,用 implementation,会把 jar 引入生成的aar中

    本地打包

  3. 使用jar,用 compileOnly,jar 源码不打包进aar中,这种模式下,如果引用的人没有再引入对应的jar包,那么会运行错误,NoClassDefFoundError 注意

    本地不打包

五、出现重复依赖或版本不一致处理方案

使用 exclude 去除,缺点如果存在多个会找到吐血,逐个添加去除

dependencies{
	//本地 aar,但对依赖进的 jar 无效
	implementation (name:'xxx',ext:'aar'){
		exclude(group: 'com.alibaba', module: 'fastjson')
	}
	//远程仓库
	implementation ('xxx:xx:1.0.0'){
		exclude(group: 'com.alibaba', module: 'fastjson')
	}
}

或者直接通过全局配置,去除指定远程仓库,除非项目不需要这个库,否则不要使用这个方法,缺点如果要用,自己也没有办法用

configurations.all {
    exclude(group: 'com.alibaba', module: 'fastjson')
}

也可以通过全局配置,修改依赖库为指定版本,可以说是最佳方案了

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == 'com.alibaba'&& requested.name == 'fastjson') {
			details.useVersion '1.2.56'
        }
    }
}

好了,本文依赖部分就到这里结束啦!谢谢阅读。

已开通微信公众号码农茅草屋,有兴趣可以关注,一起学习

在这里插入图片描述