上篇文章我们已经将 Gradle 基础运用介绍了一遍,可以这么说,只要你一直看了我这个 Gradle 系列,那么你的 Gradle 也将过关了,应对正常的工作开发已经不成问题了。
这篇文章我要向你介绍的是关于如何使用 Gradle 来更加优雅的管理多个 module 之间的依赖关系。
相信你一定有这样的经历:主项目依赖于多个子项目,或者项目间互相依赖。不同子项目间的依赖的第三方库版本又没有进行统一,升级一个版本所有依赖的项目都要进行修改;甚至 minSdkVersion 与 targetSdkVersion 也不相同。
今天我们就来解决这个问题,让 Gradle 版本管理更加优雅。
Google 推荐
之前的文章 Android Gradle 系列 - 运用篇中的 dependencies 使用的是最基本的引用方式。如果你有新建一个 kotlin 项目的经历,那么你将看到 Google 推荐的方案
buildscript {
ext.kotlin_version = '1.1.51'
repositories {google()
jcenter()}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
在 rootProject 的 build.gradle 中使用 ext 来定义版本号全局变量。这样我们就可以在 module 的 build.gradle 中直接引用这些定义的变量。引用方式如下:
dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
}
你可以将这些变量理解为 java 的静态变量。通过这种方式能够达到不同 module 中的配置统一,但局限性是,一但配置项过多,所有的配置都将写到 rootProject 项目的 build.gradle 中,导致 build.gradle 臃肿。这不符合我们的所提倡的模块开发,所以应该想办法将 ext 的配置单独分离出来。
这个时候我就要用到之前的文章 Android Gradle 系列 - 原理篇中所介绍的 apply 函数。之前的文章我们只使用了 apply 三种情况之一的 plugin(应用一个插件,通过 id 或者 class 名),只使用在子项目的 build.gradle 中。
apply plugin: 'com.android.application'
这次我们需要使用它的 from,它主要是的作用是 应用一个脚本文件。作用接下来我们需要做的是将 ext 配置单独放到一个 gradle 脚本文件中。
首先我们在 rootProject 目录下创建一个 gradle 脚本文件,我这里取名为 version.gradle。
然后我们在 version.gralde 文件中使用 ext 来定义变量。例如之前的 kotlin 版本号就可以使用如下方式实现
ext.deps = [:]
def versions = [:]
versions.support = "26.1.0"
versions.kotlin = "1.2.51"
versions.gradle = '3.2.1'
def support = [:]
support.app_compat = "com.android.support:appcompat-v7:$versions.support"
support.recyclerview = "com.android.support:recyclerview-v7:$versions.support"
deps.support = support
def kotlin = [:]
kotlin.kotlin_stdlib = "org.jetbrains.kotlin:kotlin-stdlib-jre7:$versions.kotlin"
kotlin.plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
deps.kotlin = kotlin
deps.gradle_plugin = "com.android.tools.build:gradle:$versions.gradle"
ext.deps = deps
def build_versions = [:]
build_versions.target_sdk = 26
build_versions.min_sdk = 16
build_versions.build_tools = "28.0.3"
ext.build_versions = build_versions
def addRepos(RepositoryHandler handler) {handler.google()
handler.jcenter()
handler.maven {url 'https://oss.sonatype.org/content/repositories/snapshots'}
}
ext.addRepos = this.&addRepos
因为 gradle 使用的是 groovy 语言,所以以上都是 groovy 语法
例如 kotlin 版本控制,上面代码的意思就是将有个 kotlin 相关的版本依赖放到 deps 的 kotlin 变量中,同时 deps 放到了 ext 中。其它的亦是如此。
既然定义好了,现在我们开始引入到项目中,为了让所有的子项目都能够访问到,我们使用 apply from 将其引入到 rootProject 的 build.gradle 中
buildscript {
apply from: 'versions.gradle'
addRepos(repositories)
dependencies {
classpath deps.gradle_plugin
classpath deps.kotlin.plugin
}
}
这时 build.gradle 中就默认有了 ext 所声明的变量,使用方式就如 dependencies 中的引用一样。
我们再看上面的 addRepos 方法,在关于 Gradle 原理的文章中已经分析了 repositories 会通过 RepositoryHandler 来执行,所以这里我们直接定义一个方法来统一调用 RepositoryHandler。这样我们在 build.gradle 中就无需使用如下方式,直接调用 addRepos 方法即可
// 之前调用
repositories {google()
jcenter()}
// 现在调用
addRepos(repositories)
另一方面,如果有多个 module,例如有 module1,现在就可以直接在 module1 中的 build.gradle 中使用定义好的配置
dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])
// support
implementation deps.support.app_compat
//kotlin
implementation deps.kotlin.kotlin_stdlib
}
上面我们还定义了 sdk 与 tools 版本,所以也可以一起统一使用,效果如下
android {
compileSdkVersion build_versions.target_sdk
buildToolsVersion build_versions.build_tools
defaultConfig {
applicationId "com.idisfkj.androidapianalysis"
minSdkVersion build_versions.min_sdk
targetSdkVersion build_versions.target_sdk
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
...
}
一旦实现了统一配置,那么之后我们要修改相关的版本就只需在我们定义的 version.gradle 中修改即可。无需再对所用的 module 进行逐一修改与统一配置。
BuildSrc&Kotlin
如果你的项目使用了 kotlin,那么 buildSrc&Kotlin 的统一管理方案将更适合你。
Gradle 项目会默认识别 buildSrc 目录,并且会将该目录中的配置注入到 build.gradle 中,以至于让 build.gradle 能够直接引用 buildSrc 中的配置项。
有了这一特性,我们就可以直接将之前 version.gradle 中的配置放入到 buildSrc 中,下面我们开始实现。
首先在根目录新建一个 buildSrc 目录(与 app 同级),然后在该目录新建 src/main/java 目录,该目录是你之后配置项所在的目录;同时再新建 build.gradle.kts 文件,并在该文件中添加 kotlin-dsl
plugins {`kotlin-dsl`}
repositories {jcenter()
}
之后再 sync project,最终的目录结构如下
搭建好了目录,现在我们在 src/main/java 下使用 kotlin 新建 Dependencies 文件(文件名任意),在该文件中将之前的配置项放进来,只是使用 kotlin 语法进行实现而已,转化的代码如下
object Versions {
const val support = "26.1.0"
const val kotlin = "1.3.31"
const val gradle = "3.4.1"
const val target_sdk = 26
const val min_sdk = 16
const val build_tools = "28.0.3"
}
object Dependencies {val app_compat = "com.android.support:appcompat-v7:${Versions.support}"
val kotlin_stdlib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}"
val kotlin_plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}"
val gradle_plugin = "com.android.tools.build:gradle:${Versions.gradle}"
val addRepos: (handler: RepositoryHandler) -> Unit = {it.google()
it.jcenter()
it.maven {url = URI("https://oss.sonatype.org/content/repositories/snapshots") }
}
}
这时你就可以直接使用 Dependencies 与 Versions 在各个 build.gradle 中引用,例如 app 下的 build.gradle
android {
compileSdkVersion Versions.target_sdk
buildToolsVersion Versions.build_tools
defaultConfig {
applicationId "com.idisfkj.androidapianalysis"
minSdkVersion Versions.min_sdk
targetSdkVersion Versions.target_sdk
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
...
}
dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])
// support
implementation Dependencies.app_compat
//kotlin
implementation Dependencies.kotlin_stdlib
}
根目录的 build.gralde 亦是如此
buildscript {Dependencies.addRepos.invoke(repositories)
dependencies {
classpath Dependencies.gradle_plugin
classpath Dependencies.kotlin_plugin
}
}
allprojects {Dependencies.addRepos.invoke(repositories)
}
task clean(type: Delete) {delete rootProject.buildDir}
其实我们真正需要 get 到的是一种思想,将配置统一管理。至于到底使用哪一种,这就看个人喜好了,但如果你的项目使用了 kotlin,我还是建议你使用 buildSrc 模式,因为对于 Groovy 语法而言,我相信你还是对 Kotlin 更加熟悉。
源码地址: https://github.com/idisfkj/an…
如果想了解更多关于我的文章,可以扫描下方二维码,关注我的公众号~