一键切换源码与 AAR
背景说明
针对android的开发同学来讲,我们的常用公共模块是放单独工程进行维护的,然后会打成各种SDK(jar或者aar),接着上传maven之类的,之后再通过我们的业务工程拉对应的SDK依赖,然后编译运行。
但是有没有觉得有时候如果我们要改公共模块的SDK,是不是很不方便。我们要先在公共模块改好了上传到mave,然后再业务模块拉代码调试看下对不对,如果错了接着回到公共模块继续改,是不是很麻烦,其中打包-上传-拉取这中间等待的过程很繁琐。
方案
针对上述问题,有没有一种可能,我们在开发调试过程,能不能直接不打SDK,直接业务模块依赖公共模块用来调试?当然并不是说把公共模块复制到业务模块来,毕竟这么弄工程繁琐程度不亚于原先的方式。
android常用依赖是通过implementation或者api,用来依赖本地模块或者远程模块。那我们是不是可以重写一个类似依赖implementation或者api来管理我们要选择源码依赖还是远程AAR,如果本地配置是源码依赖,那就讲远程依赖更换成本地公共模块的源码依赖。
步骤
local.properties配置要源码编译模块的路径
首先我们要先配置我们要依赖的公共模块路径,最好放到local.properties文件(不需要上传git),避免因个人配置而影响团队开发。我个人习惯把公共模块和业务模块放到同一个目录,方便查找,同时用lib-开头作为模块名,正则匹配也是按照这个规范,以下代码也是参照这个规范。
local.properties 文件
// 公共模块需要源码编译子模块相对路径
lib-common = ../公共模块工程/lib-common
settings.gradle include源码依赖的工程
接着在setting.gradle 调用include添加我们在local.properties配置需要编译的模块
settings.gradle 文件
// module.gradle是自定义脚本,需要用来读取local.properties等
apply from: "${rootDir}/buildSource/module.gradle"
rootProject.name = "MyDemo2"
include ':app'
// getLocalProperties在module.gradle
getLocalProperties().entrySet().each { entry ->
def prefix = "lib-"
if (entry.key.startsWith(prefix)) {
def path = entry.value
def file = file(path)
if (file.exists()) {
include ":$entry.key"
project(":$entry.key").projectDir = file
}
}
}
自定义脚本 module.gradle
自定义脚本module.gradle,用来读取local.property的配置信息,还有源码依赖真正的功能。
// module.gradle文件
def getLocalProperty(String key) {
try {
def value = getLocalProperties().getProperty(key)
return value == null ? "" : value
} catch (Exception e) {
e.printStackTrace()
return ""
}
}
// 获取LocalProperties对象
def getLocalProperties() {
def properties = new Properties()
try {
File localPropertiesFile
try {
localPropertiesFile = new File(rootDir, 'local.properties');
if (localPropertiesFile == null || !localPropertiesFile.exists()) {
localPropertiesFile = new File("../local.properties")
}
} catch (Exception e) {
localPropertiesFile = new File("../local.properties")
}
println("localPropertiesFile===:" + localPropertiesFile.absolutePath)
properties.load(new FileInputStream(localPropertiesFile))
return properties
} catch (Exception e) {
e.printStackTrace()
return properties
}
}
开始自定义moduleApi和moduleImplementation,用来替代api和Implementation,从而实现配置切换是否进行源码依赖
// module.gradle文件
// 以下是代码核心
// 如果配置源码依赖就走源码依赖,否则走最新的release
def moduleApi(String compileStr) {
moduleApi(compileStr, {})
}
def moduleApi(String compileStr, Closure configureClosure) {
String[] temp = compileStr.split(":")
String group = temp[0]
String artifactid = temp[1]
String version = temp[2]
Set<String> includeModule = new HashSet<>()
rootProject.getAllprojects().each {
if (it != rootProject) includeModule.add(it.name)
}
if (includeModule.contains(getFixLibName(artifactid))) {
println(project.name + "源码依赖:===project(\":${getFixLibName(artifactid)}\")")
def sourceModule = project(':' + getFixLibName(artifactid))
sourceModule.setGroup("source." + sourceModule.getGroup())
projects.project.dependencies.add("api", sourceModule, configureClosure)
projects.project.configurations { compile.exclude group: group, module: artifactid }
} else {
if (getAutoUpgradeVersion() == version) {
version = getLastVersion(group, artifactid, version.endsWith("-SNAPSHOT"))
if (!version.startsWith(baseVersion)) {
throw new RuntimeException("没有${baseVersion}对应的依赖版本,请首先编译${artifactid}模块")
}
}
println(project.name + "aar依赖:=======$group:$artifactid:$version")
projects.project.dependencies.add("api", "$group:$artifactid:$version", configureClosure)
}
}
def moduleImplementation(String compileStr) {
moduleImplementation(compileStr, {})
}
def moduleImplementation(String compileStr, Closure configureClosure) {
String[] temp = compileStr.split(":")
String group = temp[0]
String artifactid = temp[1]
String version = temp[2]
Set<String> includeModule = new HashSet<>()
rootProject.getAllprojects().each {
if (it != rootProject) includeModule.add(it.name)
}
if (includeModule.contains(getFixLibName(artifactid))) {
println(project.name + "源码依赖:===project(\":${getFixLibName(artifactid)}\")")
def sourceModule = project(':' + getFixLibName(artifactid))
sourceModule.setGroup("source." + sourceModule.getGroup())
projects.project.dependencies.add("implementation", sourceModule, configureClosure)
projects.project.configurations { compile.exclude group: group, module: artifactid }
} else {
println(project.name + "依赖:=======$group:$artifactid:$version")
projects.project.dependencies.add("implementation", "$group:$artifactid:$version", configureClosure)
}
}
private static String getFixLibName(String artifactid) {
return artifactid.startsWith("lib-") ? artifactid : "lib-" + artifactid;
}
最后还是要在module.gradle文件配置ext,把对应localProperty,moduleApi等存放ext。
// module.gradle文件
// 不要漏了
ext {
getLocalProperty = this.&getLocalProperty
getLocalProperties = this.&getLocalProperties
moduleApi = this.&moduleApi
moduleImplementation = this.&moduleImplementation
compileOnlyApi = this.&compileOnlyApi
}
使用
直接在app/build.gradle 添加依赖,就完成我们所有的功能了。要进行源码依赖,就直接在local.properties文件配置对应配置路径就可以了。
// app/build.gradle 文件
dependencies{
// 自己上传maven的AAR, 我就不提供了
moduleImplementation 'com.jeff.sdk:lib-common:1.0'
//moduleApi 'com.jeff.sdk:lib-common:1.0'
}
// local.properties
// 打开源码编译,如果关闭就注释
lib-common=../公共模块工程/lib-common
总结
以上就可以通过localProperties配置,一键切换是要走源码还是走aar依赖了,同时也方便我们调试
引用
可以参考货拉拉这篇文章 货拉拉 Android 模块化路由框架:TheRouter