用 dependencySubstitution 替换最干净,不改构建脚本、不碰源码、不影响其他模块且支持条件化切换;手动改 implementation project(...) 会引发同步修改难、本地与CI行为割裂、易误提交三大问题。

直接用 dependencySubstitution 替换最干净,不改构建脚本、不碰源码、不影响其他模块,且支持条件化切换。
为什么不用手动改 implementation project(...)
手动替换依赖看似简单,但会立刻暴露三个硬伤:一是所有引用该库的模块都得同步改 build.gradle,一漏就编译失败;二是本地源码和远程 AAR 版本不一致时,CI 构建和本地调试行为割裂;三是每次切回远程依赖都要手动还原,极易误提交。这些都不是“临时调试”,而是埋下长期维护雷。
dependencySubstitution 的正确写法(Gradle 6.0+)
它在 settings.gradle 中声明,作用于整个构建生命周期,优先级高于任何 dependencies 块。关键点在于:必须用 module 坐标匹配,不能只靠名字。
- 确保被替换的库坐标完全一致,比如
com.example:utils-core:1.2.0,连 group 和 version 都要对上 - 本地模块必须先
include,再用substitute指向它,顺序不能反 - 推荐配合
local.properties控制开关,避免误入 CI 环境
示例:
if (file("local.properties").exists()) {
def props = new Properties()
props.load(file("local.properties").newDataInputStream())
if (props.getProperty("useLocalUtils") == "true") {
include ':utils-core'
project(':utils-core').projectDir = file('../AndroidUtils/utils')
dependencySubstitution {
substitute module('com.example:utils-core') with project(':utils-core')
}
}
}
替换后为什么某些类还是没生效?
常见原因有两个:
- 被替换模块本身又依赖了旧版该库(比如
:utils-core内部还写了implementation 'com.example:utils-core:1.1.0'),形成循环或版本残留,需检查其build.gradle - 其他模块通过
api或compileOnly引入了该库,而dependencySubstitution默认只影响implementation路径,需显式加上allprojects { ... }或在对应模块中重复配置
验证是否生效,执行 ./gradlew app:dependencies --configuration releaseRuntimeClasspath,搜索目标模块名,看树形结构里是否已变成 project ':utils-core' 而非远程坐标。
真正难的是让替换逻辑在多模块、多环境、多团队协作下保持稳定——dependencySubstitution 本身不难,但它的触发条件、模块路径校验、以及与 CI/CD 流水线的隔离,才是实际落地时最容易被跳过的部分。

















