1. Android音频系统全景

1.1 依赖坐标

Gradle 使用 Maven 坐标(GAV)来唯一标识一个外部依赖。一个完整的依赖坐标由三个部分组成:group(组织/集团标识)、name(模块名称)、version(版本号)。在 build.gradle 中声明依赖时,通常写作:

implementation 'com.google.guava:guava:31.1-jre'

上面字符串格式等价于:group = 'com.google.guava', name = 'guava', version = '31.1-jre'。坐标是 Gradle 从仓库中查找依赖的唯一凭据。

坐标部分含义示例值
group组织或项目组标识,通常为反向域名com.google.guava
name模块或构件名称guava
version版本号,可包含 range 或动态版本31.1-jre
Tip: 在 Gradle 中,也可以使用 Map 形式声明依赖,如 implementation group: 'com.google.guava', name: 'guava', version: '31.1-jre',更便于动态构建坐标。

1.2 仓库配置

依赖坐标指向的构件需要从仓库中下载。Gradle 支持 Maven、Ivy 和本地文件仓库。默认情况下,Gradle 不会配置任何仓库,必须手动声明。常用仓库配置在 build.gradle 的 repositories {} 块中:

repositories {
    mavenCentral()
    google()
    maven { url 'https://jitpack.io' }
    mavenLocal()   // 本地 ~/.m2/repository
    flatDir { dirs 'libs' }  // 本地目录仓库
}
  • mavenCentral() – Maven Central 远程仓库,最常用且稳定。
  • google() – Google 的 Maven 仓库,主要用于 Android 依赖。
  • maven { url '...' } – 自定义 Maven 仓库地址。
  • mavenLocal() – 本地 Maven 缓存仓库,用于引用本地构建的构件。
  • flatDir { dirs 'libs' } – 从项目本地目录加载 JAR/AAR 文件,不依赖坐标。
Warning: 仓库配置的顺序影响依赖查找效率。通常将 mavenCentral() 放在最前,但 Google 仓库或 private 仓库若包含同名构件先被找到,可能导致版本冲突。建议明确可重复的仓库列表。

1.3 implementation vs api

Gradle 的 Java / Android 插件引入了两种主要的依赖配置:implementation 和 api。它们控制依赖是否对外泄露(即是否传递到消费者的编译 classpath)。

配置行为适用场景
implementation依赖仅对当前模块可见,不对消费者暴露其编译接口内部实现依赖,不希望在库的 API 中暴露的类型
api依赖同时暴露给当前模块和所有消费者(传递到编译 classpath)库的公开 API 中直接引用的第三方类

典型示例:

dependencies {
    // 内部使用 Gson,不暴露给使用方
    implementation 'com.google.code.gson:gson:2.9.0'

    // 注解工具 @NonNull 暴露在公共方法签名中,必须用 api
    api 'com.google.code.findbugs:jsr305:3.0.2'
}
性能建议: 优先使用 implementation,可减少重新编译时的传播范围,提升构建速度。只有那些作为公开 API 类型暴露的依赖才使用 api。

1.4 compileOnly 和 runtimeOnly

  • compileOnly – 仅在编译期可见,不会被打包或传递。常用于编译时需要的注解处理器或 Lombok。
  • runtimeOnly – 仅在运行期可见,编译时不需要。典型场景:数据库驱动(编译时只需接口,运行时需要具体实现)。

声明示例:

dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.26'
    annotationProcessor 'org.projectlombok:lombok:1.18.26'

    runtimeOnly 'mysql:mysql-connector-java:8.0.33'
}

对比总结:

配置编译 classpath运行时 classpath是否传递
compileOnly是否否
runtimeOnly否是否
implementation是是否(不对外暴露)
api是是是(暴露给消费者)

1.5 依赖声明结构图

下图展示了 Gradle 依赖声明的完整链路:从坐标形成、仓库解析,到不同配置的作用域。

Gradle依赖声明结构图 依赖坐标 group:name:version “com.google.guava:guava:31.1-jre” 仓库配置 repositories {} mavenCentral() / google() 依赖配置 dependencies { } implementation / api / ... implementation 内部依赖,不暴露 api 暴露传递 compileOnly 仅编译期 runtimeOnly 仅运行时 关键原则 • 依赖坐标 + 仓库配置 = 可解析的构件 • implementation 隐藏依赖,减少编译传播链 • compileOnly / runtimeOnly 分离编译与运行 classpath • api 用于公开 API 中直接引用的第三方类

1.6 常见错误与调试

  • 依赖无法解析:忘记配置仓库或仓库中不存在指定坐标。检查 repositories 块是否包含所需仓库。
  • 传递依赖版本冲突:多个依赖引入相同库的不同版本。使用 ./gradlew dependencies 查看依赖树,通过 force 或 constraints 统一版本。
  • 误用 api 导致编译过慢:无意中将内部依赖声明为 api,导致下游模块频繁重新编译。使用 implementation 替代。
  • compileOnly 依赖在运行时 ClassNotFoundException:确认运行时 classpath 存在相应 jar,或改用 implementation。

调试命令:

# 查看项目依赖树
./gradlew :app:dependencies

# 查看指定配置的依赖
./gradlew :app:dependencies --configuration compileClasspath

1.7 性能与稳定性优化建议

  • 优先使用 implementation 而非 api,减少模块重新编译范围。
  • 仓库配置中尽量指定 HTTPS 并启用缓存:maven { url '...'; metadataSources { mavenPom() } }。
  • 使用版本目录(Version Catalog)集中管理依赖版本,避免散落版本号。
  • 对于仅在本地开发使用的依赖(如 Lombok、QueryDSL),使用 compileOnly 避免打包体积膨胀。
实战提示: 在 Android 项目中,implementation 和 api 的区别尤为重要。如果一个库使用了 api 暴露了 AndroidX 依赖,可能导致应用模块无法独立升级版本。建议始终先尝试 implementation,遇到编译错误再升级为 api。