在我们开发安卓项目的时候, 不会所有的功能都自己去造轮子, 经常要使用到各种的其他包, 其中有谷歌给我们提供的各种 support 包, 也有各种第三方的功能库, 有时候我们自己也会将一些功能封装成包. 这些包存在和导入的形式也多种多样, 有远程仓库的, 有直接拷贝到本地的, jar 包, aar 包, so 包等. 所幸我们都可以在主工程和各个 Module 的 build.gradle 里进行统一管理. 本文将在 Android Studio3.0 环境下来汇总下这些用法.
远程仓库依赖
我们先来看下主工程下的 build.gradle 文件
- buildscript {
- ext.kotlin_version = '1.1.51'
- repositories {google()
- jcenter()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:3.0.1'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
- }
- allprojects {
- repositories {
- google()
- jcenter()
- }
- }
引入远程仓库依赖是很方便的, 但在之前我们需要声明远程仓库的地址. 上面有两个仓库地址的声明, 一个在 buildscript {}, 另一个在 repositories {}. 看代码中系统给我们的注释就知道: 前者是 gradle 脚本自身执行所需依赖 (Gradle 插件), 后者是项目本身需要的依赖(普通代码库). 所以如果你没有引入远程的 Gradle 插件, 那么就不用在 buildscript {} 下的 dependencies 下添加依赖.
关于 Gradle 插件的开发可以看下这篇文章: Gradle 自定义插件 https://blog.csdn.net/eclipsexys/article/details/50973205
再来看下几种远程依赖的添加方式:
- implementation 'commons-lang:commons-lang:2.6'
- implementation group: 'com.google.code.guice', name: 'guice', version: '1.0'
- implementation('org.hibernate:hibernate:3.1') {
- // 不同版本同时被依赖时, 那么强制依赖这个版本的, 默认 false
- force = true
- //exclude 可以设置不编译指定的模块, 有三种写法:
- exclude module: 'cglib'
- exclude group: 'org.jmock'
- exclude group: 'org.unwanted', module: 'iAmBuggy'
- // 禁止依赖的传递, gradle 自动添加子依赖项(依赖包所需的依赖), 设置为 false, 则需要手动添加每个子依赖项, 默认为 true.
- transitive = false
- }
同样的配置下的版本冲突, 会自动使用最新版; 而不同配置下的版本冲突, gradle 同步时会直接报错. 可使用 exclude,force 解决冲突. 比如你同时依赖了两个版本的 v7 包:
- implementation 'com.android.support:appcompat-v7:26.1.0'
- implementation 'com.android.support:appcompat-v7:23.1.1'
最终只会使用 26.1.0 版本. 但是如
implementation 'com.android.support:appcompat-v7:23.1.1'
, 和
androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.1'
, 所依赖的
com.android.support:support-annotations
版本不同, 就会导致冲突. 除了可以用 exclude,force 解决外, 也可以自己统一为所有依赖指定 support 包的版本, 不需要为每个依赖单独排除了:
- configurations.all {
- resolutionStrategy.eachDependency { DependencyResolveDetails details ->
- def requested = details.requested
- if (requested.group == 'com.android.support') {
- if (!requested.name.startsWith("multidex")) {
- details.useVersion '26.1.0'
- }
- }
- }
- }
编译期注解的依赖 --annotationProcessor
用过 https://github.com/JakeWharton/butterknife 或者 Dagger https://github.com/google/dagger 的同学可能对这种
annotationProcessor
引入方式有所印象, 这种方式是只在编译的时候执行依赖的库, 但是库最终不打包到 apk 中. 结合编译期注解的作用, 他是用来生成代码的, 本身在运行时是不需要的.
本地依赖
jar 包
jar 包依赖的导入还是比较简单的:
- implementation files('hibernate.jar', 'libs/spring.jar')
- // 列出每个 jar 包的相对路径
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- // 列出包含 jar 包的文件夹路径
但和远程仓库依赖引入方式不同, 如果本地同时存在两个不同的 jar 包, 或者本地已有 jar 包, 再去远程依赖不同版本的 jar 包, 就会报错.
解决方式: 将其中的一个采用 compileOnly 替换 implementation. 顾名思义, compileOnly 只在编译时起作用, 不会包含到 APK 里面, 在运行时也就避免找到重复的类了.
aar 包
和 jar 包不同, aar 包存放的路径声明和依赖引入是分开的:
- repositories {
- flatDir {
- dir "../${project.name}/libs"
- }
- }
- dependencies {
- implementation(name: 'aar 名字', ext: 'aar')
- }
如果 aar 包有很多, 也可以一样象 jar 包统一添加一个文件夹下的所有包:
- def dir = new File('app/libs')
- dir.traverse(
- nameFilter: ~/.*\.aar/
- ) { file ->
- def name = file.getName().replace('.aar', '')
- implementation(name: name, ext: 'aar')
- }
当一个 library 类型的 module 需要引用 aar 文件时, 也要在所在模块的 build.gradle 文件中加入上面的话, 但是当其他 Module 引用此 library 的 module 时, 也需要在他的 build.gradle 中加入如下配置, 否则会提示找不到文件:
- repositories {
- flatDir {
- dirs 'libs', '../ 包含 aar 包的模块名 / libs'
- }
- }
即如果当前 Module 需要一个 aar 包内容, 不论 aar 包是不是在当前 Module 中, 都需要声明它所在的路径. 如果项目中这样的模块比较多, 不便于管理的话, 也可以在项目的 build.gradle 中统一添加, 将所有包含 aar 包的模块名列出, 这样不论是本 Module 或其他 Module 都不需要单独写了:
- allprojects {
- repositories {
- jcenter()
- google()
- flatDir {
- dirs "../moudle-A/libs,../moudle-B/libs,../moudle-C/libs".split(",")
- }
- }
- }
so 文件
声明下 so 文件的存放路径就行了:
- sourceSets {
- main {
- jniLibs.srcDirs = ['libs']
- }
- }
或者直接在 main 目录下新建 jniLibs 目录, 就不需要声明了, 不过不常用.
常见问题
1.aar 包中的资源文件重复了, 在主项目中添加 build->android->添加 packagingOptions exclude 包含重复的文件
- packagingOptions {
- exclude 'META:-INF/LICENSE.txt'
- }
2.AndroidManifest 合并错误, 同样也是发生在 aar 包上, Android Studio 项目每个 module 中都可以有一个 AndroidManifest.xml 文件, 但最终的 APK 文件只能包含一个 AndroidManifest.xml 文件. 在构建应用时, Gradle 构建会将所有清单文件合并到一个封装到 APK 的清单文件中. aar 包的清单文件和我们的 app 清单文件属性冲突时: 用
tools:replace="属性名"
解决.
来源: https://juejin.im/post/5acd6daaf265da238a30ca73