本文需要对gradle、groovy的一些语法有一定的了解。本文只展示了配置管理的一种思路,不仅仅限于这么使用,希望能给大家启发
本文基于gradle-7.0.2-bin和 agp gradle:7.0.3进行开发
模块层级配置
配置声明
先来看配置的数据格式,在Components中,以集合存储key-value,既可以看成是集合,也可以看作json的Item object。其下还ext了两个脚本方法includeComponents、importDependencies,分别应用于settiing.gradle和build.gradle(:module)。对于数据格式,可根据需要,结合脚本主体一并修改,达到满足自己的使用需求
// component_config.gradle
ext {
// 这个请替换成自己远程库的group name,如com.xx.xx
COMPONENT_GROUP = 'xxx'
Components = [
[
// index(在Components中的位置,从0开始),以用于进行匹配
// 当然也可以不用这个进行匹配,选择index完全是为了从集合中取item便利
// 如有需要进行遍历匹配,可自行更改匹配item部分的脚本逻辑
index : 0,
// 模块名称,需要和对应Module中进行同步声明,脚本使用时需要传入,用于找到自身Item
module : 'corekit',
// 这个对于需要进行远程依赖时进行的path拼接,包含snapshot、version配置项
group : COMPONENT_GROUP,
// 这个是SNAPSHOT拼接开关
snapshot : true,
// 远程依赖配置版本号
version : '1.0.0.34',
// 是否需要进行本地依赖,即会被include到setting中
localEnable: false,
// 如果本地依赖的模块,与编译的app不在同一个project,那就需要声明路径,如下方不声明则默认在project中
localPath : 'Self_Demo\\corekit'
],
[
index : 1,
module : 'common',
localEnable : true,
// apiDependencies与dependencies区分,api与implementation两种依赖传递方式
apiDependencies: [0],
],
[
index : 2,
module : 'login',
localEnable : true,
dependencies: [1],
],
[
index : 3,
module : 'home',
localEnable : true,
dependencies: [1],
],
[
index : 4,
module : 'security',
localEnable : true,
dependencies: [1],
],
[
index : 5,
module : 'topic',
localEnable : true,
dependencies: [1],
],
[
index : 6,
module : 'member',
localEnable : true,
dependencies: [1],
],
[
index : 7,
module : 'category',
localEnable : true,
dependencies: [1],
],
[
index : 8,
module : 'address',
localEnable : true,
dependencies: [1],
],
[
index : 9,
module : 'search',
localEnable : true,
dependencies: [1],
],
[
index : 10,
module : 'detail',
localEnable : true,
dependencies: [1],
],
[
index : 11,
module : 'app',
dependencies: [2, 3, 4, 5, 6, 7, 8, 9, 10, 12],
// 主模块的需要设置includes标签,以进行include,同时也是主入口的标志
includes : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12]
],
[
index : 12,
module : 'barcode',
localEnable : true,
dependencies: [1]
],
]
parentPath = rootProject.projectDir.parentFile.toPath()
// 将脚本方法 对外暴露
includeComponents = this.&includeComponents
importDependencies = this.&importDependencies
}
脚本主体
findComponent用于根据moduleName进行匹配对应的Components中的Item项,以读取配置。
importDependencies用于在各自的build.gradle(:module)关联层级依赖
includeComponents用于在settiing.gradle进行module include声明,如果不需要include的话,localEnable字段为false即可,但会被以remote依赖进行import,需要在importDependencies进行区分注意
// component_config.gradle
// Closure闭包可以理解为Kotlin中的高阶函数,用于在遍历Item的子级依赖时进行相应操作
def findComponent(def moduleName, def closure) {
def component = Components.find {
// 根据project匹配处理Item
return it.module == moduleName
}
if (component && (component.dependencies || component.apiDependencies)) {
println("║ [${component.module}]---has dependencies:${if (component.dependencies) "normal{${component.dependencies}}" else ""} ${if (component.apiDependencies) "api{${component.apiDependencies}}" else ""}")
// 这块其实可以合并,但比较多,就懒得改配置了,大家可以根据需要自行改造配置的数据结构
component.dependencies.each { item ->
// 根据index,获取子级依赖,当然也可以参照 Components.find 进行匹配获取
def dependency = Components.get(item)
// 0、1是依赖模式,0为implementation,1为api
// 由于该方法闭包在两个主要脚本中均使用,所以一旦调整参数,均需修改
closure(dependency, 0)
}
component.apiDependencies.each { item ->
def dependency = Components.get(item)
closure(dependency, 1)
}
}
// 对于有需要自己做独立操作的,return出去
return component
}
// DependencyHandler就是build.gradle(:module)中的dependencies的函数体对象,对于gradle中的配置项应均理解为函数调用
def importDependencies(def moduleName, DependencyHandler dependencyHandler) {
println "╔════════════════Build Script 【${moduleName}】 Start════════════════╗"
findComponent(moduleName) {
dependency, mode ->
// 取到对于module后进行依赖导入
def path
def des
// 这里是local和remote的path拼接
if (dependency.localEnable) {
path = project(":${dependency.module}")
des = "is localed,is imported ${dependency.localPath ?: "in project"}"
} else {
def remote = "${dependency.version}${dependency.snapshot ? '-SNAPSHOT' : ''}"
path = ("${dependency.group}:${dependency.module}:$remote")
des = "is remoted,version:$remote"
}
println "║ └————component:[${dependency.module}] $des"
if (mode == 1) {
// 区分依赖模式
dependencyHandler.api path
} else {
dependencyHandler.implementation path
}
}
println "╚════════════════Build Script 【${moduleName}】 End════════════════╝"
}
// 通过includes识别主模块,进行导入
def includeComponents() {
def rootModule = Components.find {
it.includes
}
if (rootModule) {
println "╔════════════════Setting Script 【${rootModule.module}】 Start════════════════"
// 对主module单独include
include ":${rootModule.module}"
rootModule.includes.each { index ->
def dependency = Components.get(index)
if (dependency.localEnable) {
// localPath区分本地非项目中依赖,和项目中依赖
if (dependency.localPath) {
def local = "${dependency.localPath}"
println("║ └————enable local dependency:[${dependency.module}],path:$local")
include ":${dependency.module}"
project(":${dependency.module}").projectDir = new File(parentPath.toFile(), local)
} else {
println("║ └————enable local dependency:[${dependency.module}],path:in project")
include ":${dependency.module}"
}
}
}
println "╚════════════════Setting Script 【${rootModule.module}】 End════════════════"
} else {
println "can not find any root Module with 'includes' tag"
}
}
脚本引入
需要处理的其实就三处,项目gradle、模块gradle和setting文件
// build.gradle(:project)
// 此处进行全局导入
// 这个是另一个三方依赖管理,具体看下面
apply from: rootProject.file('project_config.gradle')
apply from: rootProject.file('component_config.gradle')
// setting.gradle
// 这里需要单独导入
apply from: file('component_config.gradle')
// 传入主module名称
includeComponents()
// build.gradle(:module)
def MODULE_NAME = "category"
dependencies {
importDependencies(MODULE_NAME, dependencies)
}
三方依赖管理
这个就非常简单,其实就是一些常量的声明,在使用时通过rootProject.ext进行引用即可,管理的目的是为了全局统一,避免一些不必要的依赖冲突,以及依赖升级时带来的频繁改动
// project_config.gradle
ext {
//SDK版本
compileSdkVersion = 30//用于编译的SDK版本
buildToolsVersion = '30.0.3'// 用于Gradle编译项目的工具版本
minSdkVersion = 21// 最低支持Android版本
targetSdkVersion = 30// 目标版本
versionCode = 1
versionName = '1.0'
//JDK版本
sourceCompatibilityVersion = JavaVersion.VERSION_1_8
targetCompatibilityVersion = JavaVersion.VERSION_1_8
//依赖版本
supportLibrariesVersion = '1.0.0'
dep = [
junit : 'junit:junit:4.13.1',
// for application
supportV7 : "androidx.appcompat:appcompat:1.2.0",
supportDesign : "com.google.android.material:material:1.2.1",
supportCardview : "androidx.cardview:cardview:$supportLibrariesVersion",
supportConstraintLayout: 'androidx.constraintlayout:constraintlayout:1.1.3',
]
}
// build.gradle(:module)
compileSdkVersion rootProject.ext.compileSdkVersion
dependencies {
// 如果有需要,一样可以将一组依赖作为一个集合,利用脚本进行遍历,将dependencies传入,进行操作
// 如模块层级配置时的思路一致
api dep.supportDesign
api dep.supportV7
api dep.supportCardview
api dep.supportConstraintLayout
testImplementation dep.junit
}
依赖发布
依赖打包上传maven,需要配置maven仓库,一般用Nexus管理。常量记录在gradle.properties中,在moudle中加入gradle task,对于需要使用远程依赖的project,需要进行maven引入声明
// build.gradle(:module)
task androidSourcesJar(type: Jar) {
// 如果有Kotlin那么就需要打入dir : getSrcDirs
if (project.hasProperty("kotlin")) {
from android.sourceSets.main.java.getSrcDirs()
} else if (project.hasProperty("android")) {
from android.sourceSets.main.java.sourceFiles
} else {
from sourceSets.main.allSource
}
archiveClassifier.set('sources')
}
def versionCode="1.0.0.1"
def snapshotEnable=false
afterEvaluate {
publishing {
//发布的 jar 包配置
publications {
//release这个名称可以替换,会生成对应的publishXXXPublicationToMaven的两个Task
release(MavenPublication) {
//aar 文件
from components.release
// 上传source,这样使用方可以看到方法注释,使用上面的 androidSourcesJar Task
artifact androidSourcesJar
groupId = GROUP
artifactId = MODULE
version = "$versionCode${snapshotEnable?'-SNAPSHOT':''}"
}
}
//仓库地址配置
repositories {
maven {
// 凭证
credentials {
username MAVEN_USER// 仓库发布用户名
password MAVEN_PWD // 仓库发布用户密码
}
// 地址 REPOSITORY_SNAPSHOT,REPOSITORY
url MAVEN_URL
//允许http
allowInsecureProtocol true
}
}
}
}
// gradle.properties
MAVEN_URL=http://xxxx……
MAVEN_USER=xxx
MAVEN_PWD=xxx
// build.gralde(:project)
buildscript {
maven {
// 凭证
credentials {
username MAVEN_USER// 仓库发布用户名
password MAVEN_PWD // 仓库发布用户密码
}
// 地址
url MAVEN_URL
//允许http
allowInsecureProtocol true
}
}