Gradle中的实现和编译之间有什么区别?

hum*_*zed 899 dependency-management gradle transitive-dependency build.gradle gradle-plugin

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

例:

 implementation 'com.android.support:appcompat-v7:25.0.0'
 testImplementation 'junit:junit:4.12'
Run Code Online (Sandbox Code Playgroud)

代替

 compile 'com.android.support:appcompat-v7:25.0.0'
 testCompile 'junit:junit:4.12'
Run Code Online (Sandbox Code Playgroud)

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

hum*_*zed 1162

TL;博士

只需更换:

  • compileimplementation
  • apitestCompile
  • testImplementationdebugCompile
  • debugImplementationandroidTestCompile
  • androidTestImplementation仍然有效.它在3.0中添加以替换提供而不是编译.(compileOnly当Gradle没有该用例的配置名称并在Maven提供的范围之后命名它时引入.)

这是谷歌在IO17宣布的 Gradle 3.0带来的重大变化之一.

provided配置现已弃用,应替换为compileimplementation

Gradle文档:

dependencies {
    api 'commons-httpclient:commons-httpclient:3.1'
    implementation 'org.apache.commons:commons-lang3:3.5'
}
Run Code Online (Sandbox Code Playgroud)

出现在api配置中的依赖关系将传递给库的使用者,因此将出现在使用者的编译类路径中.

api另一方面,配置中的依赖关系不会暴露给消费者,因此不会泄漏到消费者的编译类路径中.这有几个好处:

  • 依赖关系不再泄漏到消费者的编译类路径中,因此您永远不会意外地依赖于传递依赖
  • 由于减少了类路径大小,编译速度更快
  • 当实现依赖性发生变化时,重新编译的次数减少:消费者不需要重新编译
  • 清理发布:当与新的maven-publish插件结合使用时,Java库会生成POM文件,这些文件准确区分编译库所需的内容以及在运行时使用库所需的内容(换句话说,不要混合编译库本身所需的内容以及编译库所需的内容.

编译配置仍然存在,但不应使用,因为它不提供implementationapi配置提供的保证.


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

  • 是的,它现在有意义,如果你的应用程序依赖于库x,它本身依赖于y,z.如果你使用`implementation`只会暴露x api,但是如果你使用`api` y,z也会暴露出来. (218认同)
  • 谁是"消费者"? (125认同)
  • 得到它了!这更有意义了.您可以在答案中添加此说明.它比引用的文档更清晰. (32认同)
  • 使用者是使用库的模块.在Android的情况下,它是Android应用程序.我认为这很清楚,我不确定这是不是你要求的. (29认同)
  • 这对我来说也是如此.但是如果我正在创建一个库,我当然希望它的API能够暴露给应用程序.否则,应用程序开发人员将如何使用我的库?这就是为什么我没有得到隐藏依赖关系的`implementation`的含义.我的问题有意义吗? (19认同)
  • 有人可以用简单的语言解释一下吗? (3认同)
  • **如果您使用maven-publish插件将库发布到maven repo,请不要将*compile*替换为*implementation*.如果你的gradle依赖是*compile*,那么生成的pom将具有**transitive**编译范围依赖性.如果你的gradle依赖是*implementation*,那么生成的pom将具有**非传递**运行时范围依赖性. (3认同)
  • 另外,将*provided*替换为*compileOnly* (2认同)
  • @Manius第一行(大多数人首先看到的)是要求您将“ compile”替换为“ implementation”。期。我编辑了答案,说用`implementation`(如果不需要传递性)或`api`(如果需要传递性)替换compile。希望它可以防止其他人误导Find&Replace lib编译具有实现的依赖项,然后撤消所有操作。 (2认同)

ald*_*dok 322

这个答案将展示之间的差异implementation,api以及compile在一个项目.假设我有一个包含三个Gradle模块的项目:

  • app(Android应用程序)
  • myandroidlibrary(一个Android库)
  • myjavalibrary(一个Java库)

app具有myandroidlibrary依赖性.myandroidlibrary具有myjavalibrary 依赖性.

app - > myandroidlibrary - > myjavalibrary

myjavalibrary有一MySecret节课

public class MySecret {

    public static String getSecret() {
        return "Money";
    }
}
Run Code Online (Sandbox Code Playgroud)

myandroidlibrary有一个MyAndroidComponent类来操纵MySecret类的价值.

public class MyAndroidComponent {

    private static String component = MySecret.getSecret();

    public static String getComponent() {
        return "My component: " + component;
    }    
}
Run Code Online (Sandbox Code Playgroud)

最后,app只对价值感兴趣myandroidlibrary

TextView tvHelloWorld = findViewById(R.id.tv_hello_world);
tvHelloWorld.setText(MyAndroidComponent.getComponent());
Run Code Online (Sandbox Code Playgroud)

现在,我们来谈谈appbuild.gradle的依赖关系.它简单直观.:myandroidlibrary需要使用app

dependencies {
    implementation project(':myandroidlibrary')      
}
Run Code Online (Sandbox Code Playgroud)

但是,您认为implementationbuild.gradle应该是什么样子?我们应该使用哪个范围?

我们有三种选择:

dependencies {
    // Option #1
    implementation project(':myjavalibrary') 
    // Option #2
    compile project(':myjavalibrary')      
    // Option #3
    api project(':myjavalibrary')           
}
Run Code Online (Sandbox Code Playgroud)

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

编译和Api

如果你正在使用myandroidlibrarycompile.我们的Android应用程序现在能够访问api依赖项,这是一个myandroidcomponent类.

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

履行

如果您正在使用MySecret配置,implementation则不会公开.

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

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

如果要暴露依赖项使用,MySecret或者api,如果您不想公开依赖项(隐藏内部模块),则使用compile.

这只是Gradle配置的要点,请参阅表49.1.Java Library插件 - 用于声明依赖关系的配置以获得更详细的说明.

有关此答案的示例项目,请访问https://github.com/aldoKelvianto/ImplementationVsCompile

  • 我认为这应该是公认的答案。好解释! (6认同)
  • @ StevenW.Klassen是我听过的最不值得的唐纳德。如果您认为信息的顺序不是最佳的,则建议您进行编辑,而不要抱怨它 (6认同)
  • @WijaySharma接受的答案指出,“ compile”不能保证与“ api”可以保证的相同。 (2认同)

Ris*_*hav 61

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

您可以在https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation上阅读文档.

简介部分是 -

标准Java插件和Java Library插件之间的主要区别在于后者引入了向消费者公开的API的概念.库是一个Java组件,旨在供其他组件使用.这是多项目构建中非常常见的用例,但只要您有外部依赖项.

该插件公开了两种可用于声明依赖关系的配置:api和实现.api配置应该用于声明由库API导出的依赖项,而实现配置应该用于声明组件内部的依赖项.

有关进一步说明,请参阅此图像. 简要说明


Sha*_*ani 43

简要解决方案

更好的方法是compileimplementation依赖项替换所有依赖项.只有泄漏模块接口的地方,才应该使用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'
         })
 }
Run Code Online (Sandbox Code Playgroud)

解释更多:

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

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

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

  • api:您通过自己的接口泄漏此模块的接口,这意味着与旧的compile依赖项完全相同

  • implementation:您只在内部使用此模块,不会通过您的界面泄漏它

因此,如果已使用模块的接口发生变化,您现在可以明确告诉Gradle重新编译模块.

Jeroen Mols博客提供

  • 简洁明了的解释。谢谢! (2认同)

yoA*_*ex5 19

Gradle 依赖配置

Gradle 3.0 引入了下一个变化:

  • compile -> api

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

  • compile -> implementation

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

  • provided -> compileOnly

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

  • compile -> annotationProcessor

    非常类似于compileOnly但也保证传递依赖对消费者不可见

  • apk -> runtimeOnly

    依赖在编译时不可用,但在运行时可用。

【POM依赖类型】

  • 换句话说,“api = public”、“implementation = internal”和“compileOnly = private”——我需要为这些函数创建这样的别名,因为它们非常令人困惑。 (5认同)

Waj*_*Ali 11

+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| 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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
Run Code Online (Sandbox Code Playgroud)


Pom*_*m12 8

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

  • 在可能的情况下更喜欢implementation配置api

这使依赖项远离使用者的编译类路径。此外,如果任何实现类型意外泄漏到公共 API 中,消费者将立即无法编译。

那么什么时候应该使用api配置呢?API 依赖项至少包含一种在库二进制接口中公开的类型,通常称为其 ABI(应用程序二进制接口)。这包括但不限于:

  • 超类或接口中使用的类型
  • 公共方法参数中使用的类型,包括泛型参数类型(其中公共是编译器可见的东西。即 Java 世界中的公共、受保护和包私有成员)
  • 公共领域使用的类型
  • 公共注释类型

相比之下,以下列表中使用的任何类型都与 ABI 无关,因此应声明为implementation依赖项:

  • 专门用于方法体的类型
  • 专用于私有成员的类型
  • 仅在内部类中找到的类型(Gradle 的未来版本将允许您声明哪些包属于公共 API)


小智 7

外行用语的简要区别是:

  • 如果您正在开发通过公开声明的依赖项的成员为其他模块提供支持的接口或模块,则应使用“ api”。
  • 如果您要制作的应用程序或模块将在内部实现或使用声明的依赖项,请使用“实现”。
  • 'compile'与'api'的工作原理相同,但是,如果您仅实现或使用任何库,则'implementation'会更好地工作并节省资源。

阅读@aldok的答案以获取完整示例。


Nic*_*lle 6

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

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


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

所以你有蓝色的盒子compileClasspathruntimeClassPath
compileClasspath是在运行gradle build. 编译时将出现在类路径上的库将是在您的 gradle 构建中使用compileOnly或配置的所有库implementation

然后我们有了runtimeClasspath,那些都是您使用implementation或添加的包runtimeOnly。所有这些库都将添加到您部署在服务器上的最终构建文件中。

正如您在图像中看到的,如果您希望一个库既用于编译,又希望将其添加到构建文件中,则implementation应该使用它。

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

摇篮


Gau*_*egi 5

  • implementation: mostly we use implementation configuration. It hides the internal dependency of the module to its consumer to avoid accidental use of any transitive dependency, hence faster compilation and less recompilation.

  • api: must be used very carefully, since it leaks the to consumer’s compile classpath, hence misusing of api could lead to dependency pollution.

  • compileOnly: when we don’t need any dependency at runtime, since compileOnly dependency won’t become the part of the final build. we will get a smaller build size.

  • runtimeOnly: when we want to change or swap the behaviour of the library at runtime (in final build).

我创建了一篇文章,深入了解了每个工作示例:源代码

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

Gradle 配置