ChatGPT解决这个技术问题 Extra ChatGPT

Gradle 中的 implementation、api 和 compile 有什么区别?

在更新到 Android Studio 3.0 并创建一个新项目后,我注意到在 build.gradle 中有一种添加新依赖项的新方法,而不是 compile,而是 implementation,而不是 testCompile,而是 testImplementation

例子:

 implementation 'com.android.support:appcompat-v7:25.0.0'
 testImplementation 'junit:junit:4.12'

代替

 compile 'com.android.support:appcompat-v7:25.0.0'
 testCompile 'junit:junit:4.12'

它们之间有什么区别,我应该使用什么?


q
qtmfld

tl;博士

只需更换:

使用实现(如果您不需要传递性)或 api(如果您需要传递性)编译

testCompile 与 testImplementation

使用 debugImplementation 进行调试编译

androidTestCompile 与 androidTestImplementation

compileOnly 仍然有效。它是在 3.0 中添加的,用于替换提供的而不是编译的。 (在 Gradle 没有该用例的配置名称并以 Maven 提供的范围命名时引入。)

这是 Google announced at IO17 带来的 Android Gradle plugin 3.0 的重大变化之一。

compile 配置为 now deprecated,应替换为 implementationapi

Gradle documentation

依赖项 { api 'commons-httpclient:commons-httpclient:3.1' implementation 'org.apache.commons:commons-lang3:3.5' } 出现在 api 配置中的依赖项将传递给库的使用者,因此会出现在消费者的编译类路径上。另一方面,在实现配置中找到的依赖项不会暴露给消费者,因此不会泄漏到消费者的编译类路径中。这带来了几个好处:依赖项不再泄漏到消费者的编译类路径中,因此您永远不会意外地依赖传递依赖项 由于减少了类路径大小而加快了编译速度 在实现依赖项更改时减少了重新编译:消费者不需要重新编译更干净发布:与新的 maven-publish 插件一起使用时,Java 库生成 POM 文件,这些文件可以准确区分针对库编译所需的内容和在运行时使用库所需的内容(换句话说,不要混用编译库本身需要什么以及针对库编译需要什么)。 compile 配置仍然存在,但不应使用,因为它不会提供 api 和 implementation 配置提供的保证。

注意:如果您只在应用程序模块中使用库 - 常见情况 - 您不会注意到任何差异。如果您有一个复杂的项目,其中的模块相互依赖,或者您正在创建一个库,您只会看到差异。


谁是“消费者”?
消费者是使用库的模块。在 Android 的情况下,它是 Android 应用程序。我认为这很清楚,我不确定这是否是您所要求的。
我也是这么想的。但是,如果我正在创建一个库,我当然希望它的 API 能够暴露给应用程序。否则,应用程序开发人员将如何使用我的库?这就是为什么我不明白 implementation 隐藏依赖项的含义。我的问题有意义吗?
是的,如果您的应用程序依赖于库 x,而后者本身依赖于 y,z,那么现在这很有意义。如果你使用 implementation 只会暴露 x api,但如果你使用 api y,z 也会暴露。
知道了!这现在更有意义了。您可以将此解释添加到您的答案中。它比引用的文档更清楚。
a
aldok

此答案将演示项目中 implementationapicompile 之间的区别。

假设我有一个包含三个 Gradle 模块的项目:

应用程序(Android 应用程序)

myandroidlibrary(Android 库)

myjavalibrary(Java 库)

app 具有 myandroidlibrary 作为依赖项。 myandroidlibrary 具有 myjavalibrary 作为依赖项。

https://i.stack.imgur.com/GDJlK.png

myjavalibrary 有一个 MySecret

public class MySecret {

    public static String getSecret() {
        return "Money";
    }
}

myandroidlibraryMyAndroidComponent 类,它操纵来自 MySecret 类的值。

public class MyAndroidComponent {

    private static String component = MySecret.getSecret();

    public static String getComponent() {
        return "My component: " + component;
    }    
}

最后,app 只对来自 myandroidlibrary 的值感兴趣

TextView tvHelloWorld = findViewById(R.id.tv_hello_world);
tvHelloWorld.setText(MyAndroidComponent.getComponent());

现在,让我们谈谈依赖关系......

app 需要消耗 :myandroidlibrary,因此在 app build.gradle 中使用 implementation

(注意:您也可以使用 api/compile。但请稍等片刻。)

dependencies {
    implementation project(':myandroidlibrary')      
}

https://i.stack.imgur.com/qFl3Q.png

您认为 myandroidlibrary build.gradle 应该是什么样的?我们应该使用哪个范围?

我们有三个选择:

dependencies {
    // Option #1
    implementation project(':myjavalibrary') 
    // Option #2
    compile project(':myjavalibrary')      
    // Option #3
    api project(':myjavalibrary')           
}

https://i.stack.imgur.com/zXZg3.png

它们之间有什么区别,我应该使用什么?

https://i.stack.imgur.com/0thTS.png

如果您使用 compileapi。我们的 Android 应用程序现在能够访问 myandroidcomponent 依赖项,它是一个 MySecret 类。

TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can access MySecret
textView.setText(MySecret.getSecret());

实施(选项#1)

https://i.stack.imgur.com/e9bzU.png

如果您使用的是 implementation 配置,则不会公开 MySecret

TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can NOT access MySecret
textView.setText(MySecret.getSecret()); // Won't even compile

那么,您应该选择哪种配置呢?这真的取决于你的要求。

如果您想要公开依赖项,请使用 apicompile

如果您不想公开依赖项(隐藏您的内部模块),请使用 implementation

笔记:

这只是 Gradle 配置的要点,更详细的说明请参阅 Table 49.1. Java Library plugin - configurations used to declare dependencies

此答案的示例项目可在 https://github.com/aldoKelvianto/ImplementationVsCompile 上找到


我已经使用实现向一个 jar 文件添加了依赖项,如果它没有公开访问它,为什么我仍然能够获得并且我的代码工作正常?
@smkrn110 实现将公开您的 jar 库,但不会公开您的 jar 依赖库。
@WijaySharma 接受的答案指出 compile 不保证与 api 保证的相同的事情。
我认为这应该是公认的答案。很好解释!
@StevenW.Klassen 这是我听过的最不值得的反对票。如果您认为信息的顺序不是最佳的,建议编辑而不是抱怨它
M
Manuel Jordan

Compile 配置已弃用,应替换为 implementationapi

您可以阅读 API and implementation separation 部分的文档。

简短的部分是-

标准 Java 插件和 Java 库插件之间的主要区别在于后者引入了向消费者公开的 API 的概念。库是一个 Java 组件,旨在供其他组件使用。这是多项目构建中非常常见的用例,但只要您有外部依赖项,它也是如此。该插件公开了两个可用于声明依赖项的配置:api 和 implementation。 api 配置应该用于声明库 API 导出的依赖项,而 implementation 配置应该用于声明组件内部的依赖项。

https://i.stack.imgur.com/DDRqz.png


C
Community

简要解决方案:

更好的方法是将所有 compile 依赖项替换为 implementation 依赖项。只有在您泄露模块接口的地方,您才应该使用 api。这应该会导致更少的重新编译。

 dependencies {
         implementation fileTree(dir: 'libs', include: ['*.jar'])
 
         implementation 'com.android.support:appcompat-v7:25.4.0'
         implementation 'com.android.support.constraint:constraint-layout:1.0.2'
         // …
 
         testImplementation 'junit:junit:4.12'
         androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
             exclude group: 'com.android.support', module: 'support-annotations'
         })
 }

解释更多:

在 Android Gradle 插件 3.0 之前:我们遇到了一个大问题,即一次代码更改会导致所有模块重新编译。造成这种情况的根本原因是 Gradle 不知道您是否通过另一个模块泄漏了模块的接口。

在 Android Gradle 插件 3.0 之后:最新的 Android Gradle 插件现在要求您明确定义是否泄漏模块的接口。基于此,它可以对应该重新编译的内容做出正确的选择。

因此,compile 依赖项已被弃用,并被两个新的依赖项取代:

api:你通过自己的接口泄露了这个模块的接口,意思和老的compile依赖一模一样

实现:你只在内部使用这个模块,不会通过你的接口泄露它

所以现在你可以明确地告诉 Gradle 重新编译一个模块,如果一个使用的模块的接口改变了。

感谢 Jeroen Mols 博客


简洁明了的解释。谢谢!
W
Wajid
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| Name               | Role                 | Consumable? | Resolveable? | Description                             |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| api                | Declaring            |      no     |      no      | This is where you should declare        |
|                    | API                  |             |              | dependencies which are transitively     |
|                    | dependencies         |             |              | exported to consumers, for compile.     |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| implementation     | Declaring            |      no     |      no      | This is where you should                |
|                    | implementation       |             |              | declare dependencies which are          |
|                    | dependencies         |             |              | purely internal and not                 |
|                    |                      |             |              | meant to be exposed to consumers.       |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| compileOnly        | Declaring compile    |     yes     |      yes     | This is where you should                |
|                    | only                 |             |              | declare dependencies                    |
|                    | dependencies         |             |              | which are only required                 |
|                    |                      |             |              | at compile time, but should             |
|                    |                      |             |              | not leak into the runtime.              |
|                    |                      |             |              | This typically includes dependencies    |
|                    |                      |             |              | which are shaded when found at runtime. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| runtimeOnly        | Declaring            |      no     |      no      | This is where you should                |
|                    | runtime              |             |              | declare dependencies which              |
|                    | dependencies         |             |              | are only required at runtime,           |
|                    |                      |             |              | and not at compile time.                |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| testImplementation | Test dependencies    |      no     |      no      | This is where you                       |
|                    |                      |             |              | should declare dependencies             |
|                    |                      |             |              | which are used to compile tests.        |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| testCompileOnly    | Declaring test       |     yes     |      yes     | This is where you should                |
|                    | compile only         |             |              | declare dependencies                    |
|                    | dependencies         |             |              | which are only required                 |
|                    |                      |             |              | at test compile time,                   |
|                    |                      |             |              | but should not leak into the runtime.   |
|                    |                      |             |              | This typically includes dependencies    |
|                    |                      |             |              | which are shaded when found at runtime. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| testRuntimeOnly    | Declaring test       |      no     |      no      | This is where you should                |
|                    | runtime dependencies |             |              | declare dependencies which              |
|                    |                      |             |              | are only required at test               |
|                    |                      |             |              | runtime, and not at test compile time.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+

不直接回答问题
也有发展而已
如果我需要运行时和编译时间,我应该使用什么?目前,我有 implementation,后跟 runtime
y
yoAlex5

Gradle 依赖配置

Gradle 3.0 介绍了接下来的更改:

compile -> api api 关键字与 deprecated compile 相同,它为所有级别公开此依赖项

compile -> implementation 是更可取的方式,因为它有一些优点。实现仅在构建时公开上一级的依赖项(依赖项在运行时可用)。结果,您的构建速度更快(无需重新编译高于 1 级的消费者)

提供 -> compileOnly 此依赖项仅在编译时可用(该依赖项在运行时不可用)。这种依赖不能是传递的并且是.aar。它可以与编译时注释处理器[About]一起使用,并允许您减少最终输出文件

compile -> annotationProcessor 与 compileOnly 非常相似,但也保证传递依赖对消费者不可见

apk -> runtimeOnly 依赖在编译时不可用,但在运行时可用。

[POM dependency type]


换句话说,api = publicimplementation = internalcompileOnly = private - 我需要为这些函数创建这样的别名,因为它们非常令人困惑。
R
Rushabh Agarwal

外行术语的简短区别是:

如果您正在开发通过公开声明的依赖项的成员来为其他模块提供支持的接口或模块,您应该使用“api”。

如果您正在制作要在内部实现或使用所述依赖项的应用程序或模块,请使用“实现”。

'compile' 与 'api' 工作方式相同,但是,如果您只实现或使用任何库,则 'implementation' 会更好地工作并节省资源。

阅读@aldok 的答案以获得全面的示例。


但问题是,如果一个人故意来这里寻找这些问题的答案,那他终究不是门外汉。
C
Community

由于版本 5.6.3 Gradle documentation 提供了简单的经验法则来确定是否应将旧的 compile 依赖项(或新的依赖项)替换为 implementationapi 依赖项:

尽可能优先使用实现配置而不是 api 这使依赖项远离使用者的编译类路径。此外,如果任何实现类型意外泄漏到公共 API 中,消费者将立即无法编译。那么什么时候应该使用api配置呢? API 依赖项包含至少一种在库二进制接口中公开的类型,通常称为其 ABI(应用程序二进制接口)。这包括但不限于:超类中使用的类型或公共方法参数中使用的接口类型,包括泛型参数类型(其中 public 是编译器可见的东西。即 Java 中的 public、protected 和 package private 成员world) 公共字段中使用的类型 public annotation types 相反,以下列表中使用的任何类型都与 ABI 无关,因此应声明为实现依赖项: 专用于方法体的类型 专用于私有成员的类型仅在内部类中发现的类型(Gradle 的未来版本将允许您声明哪些包属于公共 API)


G
Gauraw Negi

实现:我们大多使用实现配置。它隐藏了模块对其使用者的内部依赖,以避免意外使用任何传递依赖,从而加快编译速度并减少重新编译。

api:必须非常小心地使用,因为它会泄漏到消费者的编译类路径,因此滥用 api 可能会导致依赖污染。

compileOnly:当我们在运行时不需要任何依赖时,因为 compileOnly 依赖不会成为最终构建的一部分。我们将获得更小的构建尺寸。

runtimeOnly:当我们想要在运行时(在最终构建中)更改或交换库的行为时。

我创建了一个post,深入了解每一个与 Working Example: source code

https://medium.com/@gauraw.negi/how-gradle-dependency-configurations-work-underhood-e934906752e5

https://i.stack.imgur.com/6TM4D.png


N
Nico Van Belle

继续之前的一些注意事项; compile 已弃用,文档声明您应该使用 implementation,因为 compile 将在 Gradle 版本 7.0 中删除。如果您使用 --warning-mode all 运行 Gradle 构建,您将看到以下消息;

编译配置已被弃用以进行依赖项声明。这将在 Gradle 7.0 中失败并出现错误。请改用实现配置。

仅通过查看帮助页面中的图像,它就很有意义。

因此,您有蓝色框 compileClasspathruntimeClassPath
compileClasspath 是运行 gradle build 时成功构建所必需的。编译时将出现在类路径中的库将是使用 compileOnlyimplementation 在您的 gradle 构建中配置的所有库。

然后是 runtimeClasspath,这些都是您使用 implementationruntimeOnly 添加的软件包。所有这些库都将添加到您在服务器上部署的最终构建文件中。

如图所示,如果您希望一个库既可用于编译,又希望将其添加到构建文件中,则应使用 implementation

runtimeOnly 的示例可以是数据库驱动程序。
compileOnly 的示例可以是 servlet-api。
implementation 的示例可以是 spring-core。

https://i.stack.imgur.com/cSbWr.png


G
Garry Kevin

当您在 gradle 项目中声明依赖项时,消费者 gradle 项目可以使用代码库 + 其依赖项(声明为 api)。

举个例子

我们有 1 级、2 级、3 级作为 gradle 项目。

级别 1 使用级别 2。级别 2 使用级别 3。

1 级 <- 2 级 <- 3 级

使用 api 和 implementation,我们可以控制级别 3 的类是否应该暴露给级别 1。

https://i.stack.imgur.com/aXNmY.png

这如何使构建更快:

级别 3 的任何更改。不需要重新编译级别 1。尤其是在开发中,节省时间。


M
Mahozad

其他答案解释了差异。

只需确保对于 Kotlin DSL (build.gradle.kts),函数应该有括号,并且它们的字符串参数用双引号而不是单引号括起来:

Groovy (build.gradle) 实现 'com.android.support:appcompat-v7:25.0.0' testImplementation 'junit:junit:4.12'

Kotlin (build.gradle.kts) 实现("com.android.support:appcompat-v7:25.0.0") testImplementation("junit:junit:4.12")