Android一键切换源码与 AAR

760 阅读3分钟

一键切换源码与 AAR


背景说明

   针对android的开发同学来讲,我们的常用公共模块是放单独工程进行维护的,然后会打成各种SDK(jar或者aar),接着上传maven之类的,之后再通过我们的业务工程拉对应的SDK依赖,然后编译运行。

   但是有没有觉得有时候如果我们要改公共模块的SDK,是不是很不方便。我们要先在公共模块改好了上传到mave,然后再业务模块拉代码调试看下对不对,如果错了接着回到公共模块继续改,是不是很麻烦,其中打包-上传-拉取这中间等待的过程很繁琐。 android 常规模块依赖方式.jpeg

方案

   针对上述问题,有没有一种可能,我们在开发调试过程,能不能直接不打SDK,直接业务模块依赖公共模块用来调试?当然并不是说把公共模块复制到业务模块来,毕竟这么弄工程繁琐程度不亚于原先的方式。

   android常用依赖是通过implementation或者api,用来依赖本地模块或者远程模块。那我们是不是可以重写一个类似依赖implementation或者api来管理我们要选择源码依赖还是远程AAR,如果本地配置是源码依赖,那就讲远程依赖更换成本地公共模块的源码依赖。
android 一键切换源码或者mavem的SDK依赖.jpeg

步骤

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