Skip to content

版本冲突解决

源:Gradle 官方文档 - Dependency Resolution

当多个依赖引入同一库的不同版本时,就会发生版本冲突。理解冲突原理和解决策略是依赖管理的核心技能。

版本冲突产生原因

传递依赖冲突

场景

app -> lib-a:1.0 -> common:1.0
    -> lib-b:2.0 -> common:2.0

问题common 库有两个版本:1.02.0

直接依赖冲突

kotlin
dependencies {
    implementation("com.example:library:1.0")
    implementation("com.example:library:2.0")
}

问题:同一库声明了两个版本


查看依赖树

查看所有依赖

bash
# 查看所有配置的依赖树
./gradlew :app:dependencies

# 查看特定配置
./gradlew :app:dependencies --configuration runtimeClasspath
./gradlew :app:dependencies --configuration compileClasspath

输出示例

runtimeClasspath - Runtime classpath
+--- com.example:lib-a:1.0
|    \--- com.example:common:1.0
\--- com.example:lib-b:2.0
     \--- com.example:common:2.0 -> 2.0

依赖洞察

bash
# 查看特定库的所有引用路径
./gradlew :app:dependencyInsight --dependency com.example:common

# 指定配置
./gradlew :app:dependencyInsight --dependency com.example:common --configuration runtimeClasspath

输出示例

com.example:common:2.0 (selected by rule)
   variant "runtime" [
      org.gradle.status = release (not requested)
   ]

com.example:common:1.0 -> 2.0
\--- com.example:lib-a:1.0
     \--- runtimeClasspath

com.example:common:2.0
\--- com.example:lib-b:2.0
     \--- runtimeClasspath

Gradle 默认冲突解决策略

最新版本策略

默认行为:Gradle 自动选择最新版本

common:1.0 vs common:2.0 → 选择 2.0

优点

  • 自动解决大多数冲突
  • 倾向使用新版本

缺点

  • 可能引入不兼容变更
  • 可能导致运行时错误

冲突解决策略

强制版本

全局强制

kotlin
configurations.all {
    resolutionStrategy {
        force("com.example:common:1.5")
    }
}

特定配置强制

kotlin
configurations.named("runtimeClasspath") {
    resolutionStrategy {
        force("com.example:common:1.5")
    }
}

排除传递依赖

排除特定传递依赖

kotlin
dependencies {
    implementation("com.example:lib-a:1.0") {
        exclude(group = "com.example", module = "common")
    }
}

排除所有传递依赖

kotlin
dependencies {
    implementation("com.example:lib-a:1.0") {
        isTransitive = false
    }
}

全局排除

kotlin
configurations.all {
    exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7")
}

替换依赖

依赖替换

kotlin
configurations.all {
    resolutionStrategy {
        dependencySubstitution {
            substitute(module("com.example:old-lib"))
                .using(module("com.example:new-lib:2.0"))
        }
    }
}

使用依赖约束

kotlin
dependencies {
    constraints {
        implementation("com.example:common:2.0") {
            because("统一版本到2.0")
        }
    }
}

实战案例

案例1:Kotlin 版本冲突

问题

app -> lib-a -> kotlin-stdlib:1.8.0
    -> lib-b -> kotlin-stdlib:1.9.0

解决方案1:强制版本

kotlin
configurations.all {
    resolutionStrategy {
        force("org.jetbrains.kotlin:kotlin-stdlib:1.9.22")
    }
}

解决方案2:使用 BOM

kotlin
dependencies {
    implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.9.22"))
}

案例2:OkHttp 版本冲突

问题

app -> retrofit:2.9.0 -> okhttp:4.9.0
    -> okhttp-logging:4.12.0 -> okhttp:4.12.0

解决方案:排除旧版本

kotlin
dependencies {
    implementation("com.squareup.retrofit2:retrofit:2.9.0") {
        exclude(group = "com.squareup.okhttp3", module = "okhttp")
    }
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
    implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
}

案例3:AndroidX 迁移冲突

问题

app -> new-lib -> androidx.core:core:1.12.0
    -> old-lib -> com.android.support:support-core-utils:28.0.0

解决方案:启用 Jetifier

properties
# gradle.properties
android.useAndroidX=true
android.enableJetifier=true

或排除旧版本

kotlin
dependencies {
    implementation("com.example:old-lib:1.0") {
        exclude(group = "com.android.support")
    }
}

案例4:Compose 版本冲突

问题:不同 Compose 库版本不一致

解决方案:使用 Compose BOM

kotlin
dependencies {
    val composeBom = platform("androidx.compose:compose-bom:2024.12.01")
    implementation(composeBom)
    androidTestImplementation(composeBom)
    
    // 无需指定版本
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.material3:material3")
    implementation("androidx.compose.ui:ui-tooling-preview")
}

调试技巧

可视化依赖树

使用 Android Studio

ViewTool WindowsGradle → 选择模块 → Dependencies

使用插件

kotlin
// build.gradle.kts (project)
plugins {
    id("com.github.ben-manes.versions") version "0.51.0"
}
bash
./gradlew dependencyUpdates

输出依赖报告

bash
# 生成HTML报告
./gradlew :app:htmlDependencyReport

# 报告位置
# build/reports/project/dependencies/index.html

查找冲突来源

bash
# 查看为什么选择了某个版本
./gradlew :app:dependencyInsight --dependency <group:name>

# 示例
./gradlew :app:dependencyInsight --dependency com.squareup.okhttp3:okhttp

冲突解决决策树

发现版本冲突

查看依赖树

是直接依赖冲突?
    ├─ 是 → 统一版本
    └─ 否 → 是传递依赖冲突?
        ├─ 是 → 评估影响
        │   ├─ 新版本兼容 → 使用新版本(默认)
        │   ├─ 旧版本必需 → 强制使用旧版本
        │   └─ 都不兼容 → 排除+手动指定
        └─ 否 → 检查是否有BOM可用
            ├─ 有 → 使用BOM统一版本
            └─ 无 → 使用依赖约束

最佳实践

优先使用 BOM

  • 统一相关库版本
  • 避免手动管理
  • 减少冲突

及时更新依赖

  • 定期检查更新
  • 使用 Version Catalog
  • 保持依赖最新

最小化 api 依赖

  • 减少依赖传递
  • 降低冲突风险
  • 提升构建性能

使用依赖约束

  • 推荐依赖版本
  • 不强制覆盖
  • 更灵活

文档化决策

  • 注释强制版本原因
  • 记录排除依赖原因
  • 便于团队理解

定期检查

  • 使用 dependencyUpdates 插件
  • 检查过时依赖
  • 及时处理安全漏洞

CI 验证

  • 检查依赖冲突
  • 确保构建稳定
  • 避免意外变更

常见问题

运行时 ClassNotFoundException

原因:依赖冲突导致类缺失

解决

  • 检查依赖树
  • 确认类所在库版本
  • 强制使用包含该类的版本

运行时 NoSuchMethodError

原因:使用了被移除的方法

解决

  • 查看 API 变更
  • 升级或降级依赖
  • 使用兼容版本

duplicate class 错误

原因:同一类在多个依赖中

解决

  • 排除重复的依赖
  • 使用 exclude
  • 检查是否有重命名的库

工具推荐

Gradle 版本更新插件

kotlin
plugins {
    id("com.github.ben-manes.versions") version "0.51.0"
}
bash
./gradlew dependencyUpdates

依赖分析插件

kotlin
plugins {
    id("com.autonomousapps.dependency-analysis") version "1.30.0"
}
bash
./gradlew buildHealth

Android Studio 内置工具

  • Gradle Tool Window
  • Build Analyzer
  • Dependency Viewer