我试图弄清楚在构建依赖关系时api和实现配置之间的区别。

在文档中,它说实现有更好的构建时间,但是,在一个类似的问题中看到这个评论,我想知道这是否是真的。

因为我不是Gradle的专家,我希望有人能帮助我。我已经阅读了文档,但我想知道一个简单易懂的解释。


当前回答

@matpag和@dev-bmax的回答足够清楚,让人们理解实现和api之间的不同用法。我只是想从另一个角度做一个额外的解释,希望对有同样问题的人有所帮助。

我创建了两个项目进行测试:

项目A作为一个名为“frameworks-web-gradle-plugin”的java库项目依赖于“org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE” 项目B依赖于项目A的实现'com.example.frameworks.gradle:frameworks-web-gradle-plugin:0.0.1-SNAPSHOT'

上面描述的依赖层次结构如下:

[project-b] -> [project-a] -> [spring-boot-gradle-plugin]

然后我测试了以下场景:

Make project A depends on 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE' by implementation . Run gradle dependencies command in a terminal in poject B root dir,with following screenshot of output we can see that 'spring-boot-gradle-plugin' appears in runtimeClasspath dependencies tree, but not in compileClasspath's, I think that's exactly why we can't make use of library that declared using implementation, it just won't through compilation. Make project A depends on 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE' by api Run gradle dependencies command in a terminal in poject B root dir again. Now 'spring-boot-gradle-plugin' appears both in compileClasspath and runtimeClasspath dependencies tree.

我注意到一个显著的区别是,在生产者/库项目中以实现方式声明的依赖项不会出现在消费者项目的compileClasspath中,因此我们不能在消费者项目中使用相应的库。

其他回答

来自gradle文档:

让我们看一下基于jvm的项目的一个非常简单的构建脚本。

plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.hibernate:hibernate-core:3.6.7.Final'
    api 'com.google.guava:guava:23.0'
    testImplementation 'junit:junit:4.+'
}

实现 编译项目的生产源代码所需的依赖项,这些依赖项不是项目公开的API的一部分。例如,该项目使用Hibernate进行内部持久层实现。 api 编译项目生产源代码所需的依赖项,这些依赖项是项目公开的API的一部分。例如,该项目使用了Guava,并在方法签名中公开了带有Guava类的公共接口。

请参考链接:Android Studio依赖配置可在Android开发者的官方网站。

在dependencies块中,您可以使用几种不同的依赖项配置(如上面所示的实现)之一来声明一个库依赖项。每个依赖项配置都为Gradle提供了关于如何使用依赖项的不同说明。

实现

Gradle adds the dependency to the compile classpath and packages the dependency to the build output. However, when your module configures an implementation dependency, it's letting Gradle know that you do not want the module to leak the dependency to other modules at compile time. That is, the dependency is available to other modules only at runtime. Using this dependency configuration instead of api or compile (deprecated) can result in significant build time improvements because it reduces the number of modules that the build system needs to recompile. For example, if an implementation dependency changes its API, Gradle recompiles only that dependency and the modules that directly depend on it. Most app and test modules should use this configuration.

api

Gradle adds the dependency to the compile classpath and build output. When a module includes an api dependency, it's letting Gradle know that the module wants to transitively export that dependency to other modules, so that it's available to them at both runtime and compile time. This configuration behaves just like compile (which is now deprecated), but you should use it with caution and only with dependencies that you need to transitively export to other upstream consumers. That's because, if an api dependency changes its external API, Gradle recompiles all modules that have access to that dependency at compile time. So, having a large number of api dependencies can significantly increase build time. Unless you want to expose a dependency's API to a separate module, library modules should instead use implementation dependencies.

假设你有一个应用模块,它使用lib1作为库,lib1使用lib2作为库。就像这样:app -> lib1 -> lib2。

现在当在lib1中使用api lib2时,当在app模块中使用api lib1或实现lib1时,app可以看到lib2代码。

但是当在lib1中使用实现lib2时,应用程序无法看到lib2代码。

文档中有很好的解释

api配置应该用于声明依赖关系 由库API导出,而实现配置 应用于声明依赖项,这些依赖项是 组件。

我喜欢把api依赖看作是公共的(其他模块可以看到),而实现依赖则是私有的(只有这个模块可以看到)。

注意,与公共/私有变量和方法不同,api/实现依赖关系不是由运行时强制执行的。这只是一个构建时优化,它允许Gradle知道当某个依赖项更改API时需要重新编译哪些模块。