添加构建依赖项

Android Studio 中的 Gradle 构建系统简化了将外部二进制文件或其他库模块作为依赖项加入构建的操作。 这些依赖项可能位于您的计算机上或远程代码库中,它们声明的任何传递依赖项也将自动包括在内。 本页面介绍如何在您的 Android 项目中使用依赖项,包括有关 Android Plugin for Gradle 特有行为和配置的详细信息。 如需获得更深入的 Gradle 依赖项概念性指导,您还应参阅 Gradle 依赖项管理指南,但切记,您的 Android 项目只能使用本页面上定义的依赖项配置

依赖项类型

要为您的项目添加依赖项,请在您的 build.gradle 文件的 dependencies 程序块中指定依赖项配置,例如 implementation

例如,以下这个应用模块的 build.gradle 文件包括三种不同类型的依赖项:

apply plugin: 'com.android.application'

android { ... }

dependencies {
    // Dependency on a local library module
    implementation project(":mylibrary")

    // Dependency on local binaries
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    // Dependency on a remote binary
    implementation 'com.example.android:app-magic:12.3'
}

其中每个依赖项配置都请求不同种类的库依赖项,如下所示:

本地库模块依赖项
implementation project(':mylibrary')

这段代码声明名为“mylibrary”的 Android 库模块的依赖项(该名称必须匹配使用 settings.gradle 文件中的 include: 定义的库名称)。 在构建您的应用时,构建系统会编译库模块,并将生成的编译内容打包到 APK中。

本地二进制文件依赖项
implementation fileTree(dir: 'libs', include: ['*.jar'])

Gradle 声明项目 module_name/libs/ 目录中 JAR 文件的依赖项(因为 Gradle 会读取 build.gradle 文件的相对路径)。

或者,您也可以像下面这样指定单独的文件:

implementation files('libs/foo.jar', 'libs/bar.jar')
远程二进制文件依赖项
implementation 'com.example.android:app-magic:12.3'

以上代码实际上是下列代码的缩写形式:

implementation group: 'com.example.android', name: 'app-magic', version: '12.3'

这段代码声明“com.example.android”命名空间组内“app-magic”库 12.3 版本的依赖项。

注:与此类似的远程依赖项要求您声明相应的远程代码库,Gradle 应在其中寻找该库。 如果本地尚不存在该库,Gradle 会在构建需要它时(例如,当您点击 Sync Project with Gradle Files 或当您运行构建时)从远程站点获取该库。

依赖项配置

dependencies 代码块内,您可以使用几种不同依赖项配置中的一种(例如上文所示的 implementation)声明库依赖项。 每项依赖项配置都为 Gradle 提供有关如何使用依赖项的不同说明。 下表介绍您可以在 Android 项目中对依赖项使用的每种配置。 此表还将这些配置与自 Android Gradle Plugin 3.0.0 起弃用的配置进行比较。

新配置 已弃用配置 行为
implementation compile Gradle 会将依赖项添加到编译类路径,并将依赖项打包到构建输出。但是,当您的模块配置 implementation 依赖项时,会告知 Gradle 您不想模块在编译时将依赖项泄露给其他模块。也就是说,依赖项只能在运行时供其他模块使用。

使用此依赖项配置而不是 apicompile(已弃用),可以显著缩短构建时间,因为它可以减少构建系统需要重新编译的模块数量。例如,如果 implementation 依赖项更改了其 API,Gradle 只会重新编译该依赖项和直接依赖它的模块。大多数应用和测试模块都应使用此配置。

api compile Gradle 会将依赖项添加到编译类路径,并构建输出。当模块包括 api 依赖项时,会告知 Gradle 模块想将该依赖项间接导出至其他模块,以使这些模块在运行时和编译时均可使用该依赖项。

此配置的行为类似于 compile (现已弃用),但您应仅对需要间接导出至其他上游消费者的依赖项慎重使用它。 这是因为,如果 api 依赖项更改了其外部 API,Gradle 会重新编译可以在编译时访问该依赖项的所有模块。 因此,拥有大量 api 依赖项会显著增加构建时间。 如果不想向不同的模块公开依赖项的 API,库模块应改用 implementation 依赖项。

compileOnly provided Gradle 只会将依赖项添加到编译类路径(即不会将其添加到构建输出)。如果是创建 Android 模块且在编译期间需要使用该依赖项,在运行时可选择呈现该依赖项,则此配置会很有用。

如果使用此配置,则您的库模块必须包含运行时条件,以便检查是否提供该依赖项,然后妥善更改其行为,以便模块在未提供依赖项的情况下仍可正常工作。这样做不会添加不重要的瞬时依赖项,有助于缩减最终 APK 的大小。 此配置的行为类似于 provided (现已弃用)。

runtimeOnly apk Gradle 只会将依赖项添加到构建输出,供运行时使用。也就是说,不会将其添加到编译类路径。 此配置的行为类似于 apk(现已弃用)。
annotationProcessor compile 要在库中添加注解处理器依赖项,则必须使用 annotationProcessor 配置将其添加到注解处理器类路径。这是因为使用此配置可分离编译类路径与注解处理器类路径,从而提升构建性能。如果 Gradle 在编译类路径上找到注解处理器,则会停用 避免编译功能,这样会增加构建时间(Gradle 5.0 和更高版本会忽略编译类路径上的注解处理器)。

如果 JAR 文件包含以下文件,则 Android Gradle Plugin 会假定依赖项是注解处理器:
META-INF/services/javax.annotation.processing.Processor。 如果插件检测到编译类路径上包含注解处理器,则会生成构建错误。

以上配置适用于您的项目的主源集,该源集应用于所有构建不同类型。 如果您改为只想为特定构建不同类型源集或测试源集声明依赖项,则必须大写配置名称并在其前面加上构建不同类型或测试源集的名称作为前缀。

例如,要仅将 implementation 依赖项添加到您的“free”产品风格(使用远程二进制文件依赖项),需要使用下面这样的代码:

dependencies {
    freeImplementation 'com.google.firebase:firebase-ads:9.8.0'
}

但如果想要为组合产品风格构建类型的变体添加依赖项,则必须在 configurations 代码块中初始化配置名称。 以下示例向您的“freeDebug”构建变体添加 runtimeOnly 依赖项(使用本地二进制文件依赖项):

configurations {
    // Initializes a placeholder for the freeDebugRuntimeOnly dependency
    // configuration.
    freeDebugRuntimeOnly {}
}

dependencies {
    freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
}

要为您的本地测试和设备化测试添加 implementation 依赖项,需要使用下面这样的代码:

dependencies {
    // Adds a remote binary dependency only for local tests.
    testImplementation 'junit:junit:4.12'

    // Adds a remote binary dependency only for the instrumented test APK.
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

但某些配置在这种情况下没有意义。 例如,由于其他模块无法依赖 androidTest,因此如果使用 androidTestApi 配置,则会收到以下警告:

WARNING: Configuration 'androidTestApi' is obsolete and has been replaced with
'androidTestImplementation'.

添加注解处理器

如果将注解处理器添加到您的编译类路径,您将看到一条与以下消息类似的错误消息:

Error: Annotation processors must be explicitly declared now.

要解决此错误问题,请使用 annotationProcessor 配置您的依赖项,以在您的项目中添加注解处理器,如下所示:

dependencies {
    // Adds libraries defining annotations to only the compile classpath.
    compileOnly 'com.google.dagger:dagger:version-number'
    // Adds the annotation processor dependency to the annotation processor classpath.
    annotationProcessor 'com.google.dagger:dagger-compiler:version-number'
}

注:Android Plugin for Gradle 3.0.0+ 不再支持 android-apt 插件。

向注解处理器传递参数

如果需要向注解处理器传递参数,您可以在您的模块构建配置中使用 AnnotationProcessorOptions 代码块。 例如,如果要以键值对形式传递原始数据类型,则可使用 argument 属性,如下所示:

android {
    ...
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                argument "key1", "value1"
                argument "key2", "value2"
            }
        }
    }
}

但在使用 Android Gradle Plugin 3.2.0 和更高版本时,您需要使用 Gradle CommandLineArgumentProvider 接口传递表示文件或目录的 处理器参数。

使用 CommandLineArgumentProvider 可让您或注解处理器作者将增量构建属性类型注解应用于每个参数,从而提高增量构建和缓存干净构建的正确性 和性能。

例如,下面的类可实现 CommandLineArgumentProvider 并注解处理器的每个参数。 此外,此示例也使用 Groovy 语言语法,且直接包含在模块的 build.gradle 文件中。

class MyArgsProvider implements CommandLineArgumentProvider {

    // Annotates each directory as either an input or output for the
    // annotation processor.
    @InputFiles
    // Using this annotation helps Gradle determine which part of the file path
    // should be considered during up-to-date checks.
    @PathSensitive(PathSensitivity.RELATIVE)
    FileCollection inputDir

    @OutputDirectory
    File outputDir

    // The class constructor sets the paths for the input and output directories.
    MyArgsProvider(FileCollection input, File output) {
        inputDir = input
        outputDir = output
    }

    // Specifies each directory as a command line argument for the processor.
    // The Android plugin uses this method to pass the arguments to the
    // annotation processor.
    @Override
    Iterable<String> asArguments() {
        // Use the form '-Akey[=value]' to pass your options to the Java compiler.
        ["-AinputDir=${inputDir.singleFile.absolutePath}",
         "-AoutputDir=${outputDir.absolutePath}"]
    }
}

android {...}

在创建实现 CommandLineArgumentProvider 的类后,您需要使用 annotationProcessorOptions.compilerArgumentProvider 属性初始化并将其传递至 Android 插件,如下所示。

// This is in your module's build.gradle file.
android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                // Creates a new MyArgsProvider object, specifies the input and
                // output paths for the constructor, and passes the object
                // to the Android plugin.
                compilerArgumentProvider new MyArgsProvider(files("input/path"),
                                         new File("output/path"))
            }
        }
    }
}

如需了解有关实现 CommandLineArgumentProvider 如何帮助提高构建性能的详细信息,请阅读缓存 Java 项目

停用注解处理器错误检查

如果编译类路径中的依赖项包含您不需要的注解处理器,您可以将以下代码添加到 build.gradle 文件中,停用错误检查。 请记住,您添加到编译类路径中的注解处理器仍不会添加到处理器类路径中。

android {
    ...
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                includeCompileClasspath false
            }
        }
    }
}

如果您将项目的注解处理器迁移到处理器类路径后遇到问题,可通过将 includeCompileClasspath 设置为 true,允许编译类路径上包含注解处理器。 但是,我们不建议将此属性设置为 true,并且我们将在以后的 Android plugin 更新版本中移除这种操作的相关选项。

排除传递依赖项

随着应用范围的扩大,其中可包含许多依赖项,包括直接依赖项和传递依赖项(应用的导入库所依赖的库)。 要排除不再需要的传递依赖项,您可以使用 exclude 关键字,如下所示:

dependencies {
    implementation('some-library') {
        exclude group: 'com.example.imgtools', module: 'native'
    }
}

从测试配置中排除传递依赖项

如果需要从您的测试中排除某些传递依赖项,上文所示的代码示例可能无法按预期发挥作用。 这是因为测试配置(例如 androidTestImplementation)扩展了模块的 implementation 配置。 也就是说,在 Gradle 解析配置时其中始终包含 implementation 依赖项。

因此,要从测试中排除传递依赖项,必须在执行代码时执行此操作,如下所示:

android.testVariants.all { variant ->
    variant.getCompileConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
    variant.getRuntimeConfiguration().exclude group: 'com.jakewharton.threetenabp', module: 'threetenabp'
}

注:您仍可在排除依赖项部分的原始代码示例所示的依赖项块中使用 exclude 关键字,以忽略特定于测试配置的传递依赖项,以及其他配置不包含的传递依赖项。

使用 variant-aware 依赖项管理

Android 插件 3.0.0 及更高版本包含一项新的依赖项机制,这种机制可以在消费库时自动匹配不同类型。 也就是说,应用的 debug 不同类型将自动消费库的 debug 不同类型,依此类推。 这种机制也适用于使用风格的情况—应用的 freeDebug 变体将使用库的 freeDebug 变体。

要让插件准确匹配变体,您需要为无法直接匹配的情况提供匹配回退。 假设您的应用配置一个名为“staging”的构建类型,但其库依赖项之一没有进行相应配置。 在插件尝试构建您的“staging”版本的应用时,它将无法了解库要使用哪一个版本,您将看到一条类似以下消息的错误消息:

Error:Failed to resolve: Could not resolve project :mylibrary.
Required by:
    project :app

解决与变体匹配相关的构建错误

插件包含 DSL 元素,这些元素有助于控制 Gradle 应如何解决应用与依赖项之间无法实现直接变体匹配的情况。 请通过查阅下表来确定应使用哪个 DSL 属性来解决与 variant-aware 依赖项匹配相关的特定构建错误。

构建错误原因解决方法

您的应用包含库依赖项不包含的构建类型。

例如,您的应用包含“staging”构建类型,但依赖项仅包含“调试”和“发布”构建类型。

请注意,如果库依赖项包含您的应用不包含的构建类型,这不会引发问题。 这是因为插件在任何时候都不会从依赖项请求该构建类型。

使用 matchingFallbacks 为给定构建类型指定替代匹配,如下所示:

// In the app's build.gradle file.
android {
    buildTypes {
        debug {}
        release {}
        staging {
            // Specifies a sorted list of fallback build types that the
            // plugin should try to use when a dependency does not include a
            // "staging" build type. You may specify as many fallbacks as you
            // like, and the plugin selects the first build type that's
            // available in the dependency.
            matchingFallbacks = ['debug', 'qa', 'release']
        }
    }
}

对于应用及其库依赖项中均存在的给定风格维度,您的应用包含库不包含的风格。

例如,您的应用及其库依赖项都包含“级别”风格维度。 不过,应用中的“级别”维度包含“免费”和“付费”风格,但依赖项仅包含相同维度的“演示”和“付费”风格。

请注意,对于应用及其库依赖项中均存在的给定风格维度,如果库包含应用不包含的产品风格,这不会引发问题。 这是因为插件在任何时候都不会从依赖项请求该风格。

使用 matchingFallbacks 为应用的“免费”产品特点指定替代匹配,如下所示:

// In the app's build.gradle file.
android {
    defaultConfig{
    // Do not configure matchingFallbacks in the defaultConfig block.
    // Instead, you must specify fallbacks for a given product flavor in the
    // productFlavors block, as shown below.
  }
    flavorDimensions 'tier'
    productFlavors {
        paid {
            dimension 'tier'
            // Because the dependency already includes a "paid" flavor in its
            // "tier" dimension, you don't need to provide a list of fallbacks
            // for the "paid" flavor.
        }
        free {
            dimension 'tier'
            // Specifies a sorted list of fallback flavors that the plugin
            // should try to use when a dependency's matching dimension does
            // not include a "free" flavor. You may specify as many
            // fallbacks as you like, and the plugin selects the first flavor
            // that's available in the dependency's "tier" dimension.
            matchingFallbacks = ['demo', 'trial']
        }
    }
}

库依赖项包含应用不包含的风格维度。

例如,库依赖项包含“minApi”维度的风格, 但您的应用包含仅适用于“级别”维度的风格。 因此,当您想要构建“freeDebug”版本的应用时,插件不知道使用“minApi23Debug”还是“minApi18Debug” 版本的依赖项。

请注意,如果应用包含库依赖项不包含的风格维度,这不会引发问题。 这是因为插件仅会匹配依赖项中存在的维度的风格。 例如,如果依赖项不包含 ABI 的维度,您的“freeX86Debug”版本的应用将直接使用“freeDebug” 版本的依赖项。

defaultConfig 代码块中使用 missingDimensionStrategy 指定插件应从每个缺失维度中选择的默认风格,如下面的示例所示。 您也可以替换在 productFlavors 代码块中的选择,让每种风格都可以为缺失维度指定一个不同的匹配策略。

// In the app's build.gradle file.
android {
    defaultConfig{
    // Specifies a sorted list of flavors that the plugin should try to use from
    // a given dimension. The following tells the plugin that, when encountering
    // a dependency that includes a "minApi" dimension, it should select the
    // "minApi18" flavor. You can include additional flavor names to provide a
    // sorted list of fallbacks for the dimension.
    missingDimensionStrategy 'minApi', 'minApi18', 'minApi23'
    // You should specify a missingDimensionStrategy property for each
    // dimension that exists in a local dependency but not in your app.
    missingDimensionStrategy 'abi', 'x86', 'arm64'
    }
    flavorDimensions 'tier'
    productFlavors {
        free {
            dimension 'tier'
            // You can override the default selection at the product flavor
            // level by configuring another missingDimensionStrategy property
            // for the "minApi" dimension.
            missingDimensionStrategy 'minApi', 'minApi23', 'minApi18'
        }
        paid {}
    }
}

配置 Wear OS 应用依赖项

配置 Wear OS 模块的依赖项与配置任何其他模块的依赖项相似。 也就是说,它们使用的依赖项配置相同,比如 implementationcompileOnly

Wear 模块还支持 variant-aware 依赖项管理。 因此,如果应用基础模块的依赖项位于 Wear 模块上,则基础模块的每个变体都会使用 Wear 模块中匹配的变体。 如果构建依赖项仅在一个 Wear 模块上的简单应用,而且此模块配置的变体与基础模块相同,则需要在基础模块的 build.gradle 文件中指定 wearApp 配置,如下所示:

dependencies {
    // If the main and Wear app modules have the same variants,
    // variant-aware dependency management automatically matches
    // variants of the main app module with that of the wear module.
    wearApp project(':wearable')
}

如果有多个 Wear 模块,并且您想要按应用风格指定不同的 Wear 模块,则可使用 flavorWearApp 配置执行此操作,如下所示(但不能包含其他使用 wearApp 配置的依赖项):

dependencies {
    paidWearApp project(':wear1')
    demoWearApp project(':wear1')
    freeWearApp project(':wear2')
}

远程代码库

如果您的依赖项并非本地库或文件树,Gradle 会在您的 build.gradle 文件 repositories 程序块中指定的任何一个在线代码库中寻找文件。 列出各代码库的顺序决定了 Gradle 在这些代码库中搜索各项目依赖项的顺序。 例如,如果代码库 A 和 B 都提供某依赖项,而您先列出代码库 A,则 Gradle 会从代码库 A 下载此依赖项。

默认情况下,Android Studio 新项目会在项目的顶级 build.gradle 文件中指定 Google 的 Maven 代码库和 JCenter 作为代码库位置,如下所示:

allprojects {
    repositories {
        google()
        jcenter()
    }
}

如果您需要的内容来自 Maven 中央代码库,则添加 mavenCentral();如果来自本地代码库,则使用 mavenLocal()

allprojects {
    repositories {
        google()
        jcenter()
        mavenCentral()
        mavenLocal()
    }
}

或者,也可像下面这样声明特定 Maven 或 Ivy 代码库:

allprojects {
    repositories {
        maven {
            url "https://repo.example.com/maven2"
        }
        maven {
            url "file://local/repo/"
        }
        ivy {
            url "https://repo.example.com/ivy"
        }
    }
}

如需了解详细信息,请参阅 Gradle 代码库指南

Google 的 Maven 代码库

Google 的 Maven 代码库中提供了下列 Android 库的最新版本:

您可以在 Google 的 Maven 代码库索引查看所有可用的项目(请参阅下文有关编程式访问的内容)。

要将其中一个库添加到您的构建,请在您的顶级 build.gradle 文件中加入 Google 的 Maven 代码库:

allprojects {
    repositories {
        google()

        // If you're using a version of Gradle lower than 4.1, you must instead use:
        // maven {
        //     url 'https://maven.google.com'
        // }
        // An alternative URL is 'https://dl.google.com/dl/android/maven2/'
    }
}

然后将所需库添加到您的模块的 dependencies 程序块。 例如,appcompat 库如下所示:

dependencies {
    implementation 'com.android.support:appcompat-v7:28.0.0'
}

但如果您尝试使用以上库的旧版本时依赖项失败,则表明 Maven 代码库中未提供该版本,您必须改为从离线代码库获取该库。

编程式访问

如需编程式访问 Google 的 Maven 项目,您可以从 maven.google.com/master-index.xml 获取项目组的 XML 列表。 然后,对于任何组,您都可以在以下位置查看其库名称和版本:

maven.google.com/group_path/group-index.xml

例如,android.arch.lifecycle 组中的库列于 maven.google.com/android/arch/lifecycle/group-index.xml 中。

您还可以在以下位置下载 POM 和 JAR 文件:

maven.google.com/group_path/library/version /library-version.ext

例如:maven.google.com/android/arch/lifecycle/compiler/1.0.0/compiler-1. 0.0.pom

来自 SDK 管理器的离线代码库

对于 Google Maven 代码库中未提供的库(通常是旧版本库),您必须从 SDK 管理器下载离线 Google 代码库软件包。

然后您就可以照常将这些库添加到您的 dependencies 程序块。

离线库保存在 android_sdk/extras/ 中。

依赖项顺序

您列出依赖项的顺序表示各依赖项的优先级: 第一个库的优先级高于第二个,第二个的优先级高于第三个,依此类推。 从库合并资源合并 manifest 元素到您的应用中时,此顺序具有重要意义。

例如,如果您的项目声明以下内容:

  • LIB_ALIB_B 上的依赖项(按照该顺序)
  • 并且 LIB_A 依赖 LIB_CLIB_D (按照该顺序)
  • 并且 LIB_B 还依赖 LIB_C

然后,扁平型依赖项顺序将如下所示:

  1. LIB_A
  2. LIB_D
  3. LIB_B
  4. LIB_C

这可以确保 LIB_ALIB_B 都能替换 LIB_C;并且 LIB_D 的优先级仍高于 LIB_B,因为 LIB_A(依赖它)的优先级高于 LIB_B

如需了解有关如何合并不同项目源/依赖项 manifest 的详细信息,请参阅合并多个 manifest 文件

查看依赖项树

某些直接依赖项可能会有其自己的依赖项。 这些依赖项称为传递依赖项。 Gradle 将会自动为您收集并添加这些传递依赖项,无需您手动逐一加以声明。 为直观地呈现您的项目的直接和传递依赖项,Android Plugin for Gradle 提供了一项 Gradle 任务,可为每个构建不同类型测试源集生成依赖项树。

要运行该任务,请执行如下操作:

  1. 选择 View > Tool Windows > Gradle(或点击工具窗口栏中的 Gradle )。
  2. 展开 AppName > Tasks > android 并双击 androidDependencies。 Gradle 执行任务后,Run 窗口应随即打开以显示输出。

以下示例输出显示了调试构建不同类型的依赖项树,并包含前一示例中的本地库模块依赖项和远程依赖项。

Executing tasks: [androidDependencies]
:app:androidDependencies
debug
/**
 * Both the library module dependency and remote binary dependency are listed
 * with their transitive dependencies.
 */
+--- MyApp:mylibrary:unspecified
|    \--- com.android.support:appcompat-v7:28.0.0
|         +--- com.android.support:animated-vector-drawable:28.0.0
|         |    \--- com.android.support:support-vector-drawable:28.0.0
|         |         \--- com.android.support:support-v4:28.0.0
|         |              \--- LOCAL: internal_impl-28.0.0.jar
|         +--- com.android.support:support-v4:28.0.0
|         |    \--- LOCAL: internal_impl-28.0.0.jar
|         \--- com.android.support:support-vector-drawable:28.0.0
|              \--- com.android.support:support-v4:28.0.0
|                   \--- LOCAL: internal_impl-28.0.0.jar
\--- com.android.support:appcompat-v7:28.0.0
     +--- com.android.support:animated-vector-drawable:28.0.0
     |    \--- com.android.support:support-vector-drawable:28.0.0
     |         \--- com.android.support:support-v4:28.0.0
     |              \--- LOCAL: internal_impl-28.0.0.jar
     +--- com.android.support:support-v4:28.0.0
     |    \--- LOCAL: internal_impl-28.0.0.jar
     \--- com.android.support:support-vector-drawable:28.0.0
          \--- com.android.support:support-v4:28.0.0
               \--- LOCAL: internal_impl-28.0.0.jar
...

如需了解在 Gradle 中管理依赖项的详细信息,请参阅《Gradle 用户指南》中的依赖项管理基础知识

修复依赖项解析错误

在向应用项目添加多个依赖项时,这些直接和传递依赖项可能会彼此冲突。 Android Gradle Plugin 会尝试妥善解决这些冲突问题,但某些冲突可能会导致编译时间或运行时错误。

为帮助您调查是哪些依赖项导致错误,请检查您的应用的依赖项树并寻找多次出现的依赖项或版本冲突的依赖项。

如果无法轻易确定重复的依赖项,请按以下步骤尝试使用 Android Studio 的界面搜索包含重复类的依赖项:

  1. 选择菜单栏中的 Navigate > Class
  2. 在弹出式搜索对话框中,确保 Include non-project items 旁的方框已勾选。
  3. 键入出现在构建错误中的类的名称。
  4. 检查包括该类的依赖项的结果。

以下部分介绍您可能遇到的不同类型的依赖项解析错误及其修复方法。

修复重复类错误

如果某类多次出现在运行时类路径中,您会收到一条与以下内容相似的错误:

Program type already present com.example.MyClass

该错误通常是下列其中一种情况所致:

  • 二进制文件依赖项包括您的应用同时作为直接依赖项包括的库。 例如,您的应用在库 A 和库 B 上声明了直接依赖项,但库 A 的二进制文件中已包括库 B。
    • 要解决此问题,请取消将库 B 作为直接依赖项。
  • 您的应用在同一库上具有本地二进制文件依赖项和远程二进制文件依赖项。
    • 要解决此问题,请移除其中一个二进制文件依赖项。

解决类路径之间的冲突问题

当 Gradle 解析编译类路径时,会先解析运行时类路径,然后使用此结果确定应添加到编译类路径的依赖项版本。 换言之,运行时类路径决定下游类路径的相同依赖项所需的版本号。

应用的运行时类路径还决定 Gradle 匹配运行类路径中应用测试 APK 的依赖项所需要的版本号。 图 1 介绍类路径的层级。

图 1. 必须根据此层级匹配出现在多个类路径中的依赖项版本号。

如果相同依赖项的冲突版本出现在多个类路径中,您可能会看到与以下内容相似的错误:

Conflict with dependency 'com.example.library:some-lib:2.0' in project 'my-library'.
Resolved versions for runtime classpath (1.0) and compile classpath (2.0) differ.

例如,当您的应用使用 implementation 依赖项配置加入某依赖项版本,并且库模块使用 runtimeOnly 配置加入此依赖项的不同版本时,可能会发生该冲突。 要解决此问题,请执行以下其中一项操作:

  • 将所需版本的依赖项作为 api 依赖项加入您的库模块。 也就是说,仅库模块声明此依赖项,但应用模块也可间接访问其 API。
  • 或者,您也可以同时在两个模块中声明此依赖项,但应确保每个模块使用的版本相同。 请考虑配置项目范围的属性,以确保各依赖项的多个版本在整个项目中都保持一致。

应用自定义构建逻辑

本节介绍的高级主题在您想要扩展 Android Gradle Plugin 或编写自己的插件时很有用。

为自定义逻辑发布变体依赖项

库可以包含其他项目或子项目可能要使用的功能。 发布库是为其消费者提供库的流程。 库可以控制其消费者在编译时和运行时可访问的依赖项。

现有两种不同的配置,其中包含消费者为使用库而必须使用的各类路径的传递依赖项,如下所述:

  • variant_nameApiElements:此配置包含编译时消费者可使用的传递依赖项。
  • variant_nameRuntimeElements:此配置包含运行时消费者可使用的传递依赖项。

如需了解有关不同配置之间关系的详细信息,请转到 Java 库插件配置

自定义依赖项解析策略

项目包含的依赖项可能包含在相同库的两个不同版本中,这样会导致依赖项冲突。 例如,如果您的项目依赖于模块 A 的版本 1 和模块 B 的版本 2,模块 A 间接依赖于模块 B 的版本 3,则会出现依赖项版本冲突。

要解决此冲突问题,Android Gradle Plugin 需使用以下依赖项解析策略:当插件检测到依赖图中包含相同模块的不同版本时,会默认选择版本最高的模块。

但此策略可能无法按预期发挥作用。 要自定义依赖项解析策略,请使用以下配置解析您任务所需变体的特定依赖项:

  • variant_nameCompileClasspath:此配置包含适用于给定变体编译类路径的解析策略。
  • variant_nameRuntimeClasspath:此配置包含适用于给定变体运行时类路径的解析策略。

Android Gradle Plugin 包含可用于访问各变体配置对象的 getter。 因此,您可以使用变体 API 查询依赖项解析策略,如下例所示:

android {
    applicationVariants.all { variant ->
        // Return compile configuration objects of a variant.
        variant.getCompileConfiguration().resolutionStrategy {
        // Use Gradle's ResolutionStrategy API
        // to customize how this variant resolves dependencies.
            ...
        }
        // Return runtime configuration objects of a variant.
        variant.getRuntimeConfiguration().resolutionStrategy {
            ...
        }
        // Return annotation processor configuration of a variant.
        variant.getAnnotationProcessorConfiguration().resolutionStrategy {
            ...
        }
    }
}