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

时间:2017-06-12 07:13:13

标签: gradle build.gradle dependency-management gradle-plugin transitive-dependency

在更新到Android Studio 3.0并创建新项目后,我注意到在build.gradle中有一种新的方式来添加新的依赖项,而不是compileimplementation而不是testCompiletestImplementation

示例:

 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'

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

10 个答案:

答案 0 :(得分:997)

<强> TL;博士

只需替换:

  • compileimplementation(如果您不需要传递性)或api(如果您需要传递性)
  • testCompiletestImplementation
  • debugCompiledebugImplementation
  • androidTestCompileandroidTestImplementation
  • compileOnly仍然有效。它在3.0中添加以替换提供而不是编译。 (provided在Gradle没有该用例的配置名称时引入,并在Maven提供的范围之后命名。)

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

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

来自Gradle documentation

dependencies {
    api 'commons-httpclient:commons-httpclient:3.1'
    implementation 'org.apache.commons:commons-lang3:3.5'
}
     

api配置中出现的依赖关系将是   传递给图书馆的消费者,因此会   出现在消费者的编译类路径上。

     

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

     
      
  • 依赖关系不再泄漏到消费者的编译类路径中,因此您永远不会意外地依赖于传递   依赖
  •   
  • 由于减少了类路径大小,编译速度更快
  •   
  • 当实现依赖关系发生变化时,重新编译的次数减少:消费者不需要重新编译
  •   
  • 清洁发布:当与新的maven-publish插件结合使用时,Java库会生成POM文件   准确区分编译所需的内容   库以及在运行时使用库所需的内容(在其他方面)   单词,不要混合编译库本身所需的东西和什么   需要针对库进行编译。)
  •   
     

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

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

答案 1 :(得分:249)

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

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

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

appmyandroidlibrary作为依赖项。 myandroidlibrary已将myjavalibrary作为依赖项。

Dependency1

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')      
}

Dependency2

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

我们有三个选择:

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

Dependency3

  

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

编译或Api(选项#2或#3) Dependency4

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

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

实施(选项#1)

Dependency5

如果您使用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

上找到

答案 2 :(得分:57)

Compile配置已弃用,应由implementationapi替换。

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

简短部分是 -

  

标准Java插件和Java之间的关键区别   库插件是后者引入API的概念   接触消费者。库是一个Java组件   被其他组件消耗。它是一个非常常见的用例   多项目构建,但一旦你有外部   的依赖关系。

     

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

有关详细说明,请参阅此图片。 Brief explanation

答案 3 :(得分:43)

简要解决方案:

更好的方法是用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依赖项完全相同

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

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

Jeroen Mols博客提供

答案 4 :(得分:14)

Gradle 3.0引入了以下更改:

  • compile-> api

    api关键字与不推荐使用的compile

  • compile-> implementation

    首选的方式,因为它具有一些优势。 implementation仅在构建时公开一级升级的依赖项(该依赖项在运行时可用)。因此,您的构建速度更快(无需重新编译高于1级的使用者)

  • provided-> compileOnly

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

  • compile-> annotationProcessor

    compileOnly非常相似,但也保证对消费者不可见传递依存关系

  • apk-> runtimeOnly

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

[POM dependency type]

答案 5 :(得分:6)

外行人任期的短暂差异是:

  • 如果您正在使用通过公开所述依赖关系的成员来为其他模块提供支持的接口或模块,那么您应该使用'api'。
  • 如果您正在制作要在内部实施或使用所述依赖关系的应用程序或模块,请使用“实施”。
  • 'compile'与'api'的工作方式相同,但是,如果您只是实现或使用任何库,'实施'将更好地工作并节省您的资源。

阅读@aldok的答案,作为一个全面的例子。

答案 6 :(得分:6)

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

<块引用>

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


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

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

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

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

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

Gradle

答案 7 :(得分:2)

从版本5.6.3 Gradle documentation开始,我们提供了简单的经验法则来确定是将旧的compile依赖项(还是新的依赖项)替换为implementation或{{1 }}依赖项:

  
      
  • 在可能的情况下,最好在api上实现配置
  •   
     

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

     

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

     
      超级类或接口中使用的
  • 类型
  •   
  • 用于公共方法参数的类型,包括通用参数类型(其中public是编译器可见的东西。即Java世界中的public,protected和package private成员)
  •   
  • 公共领域中使用的类型
  •   
  • 公共注释类型
  •   
     

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

     
      
  • 专门用于方法主体的类型
  •   
  • 专用于私人成员的类型
  •   
  • 内部类中独有的类型(将来的Gradle版本将让您声明哪些包属于公共API)
  •   

答案 8 :(得分:1)

  • 实施:大多数情况下,我们使用实施配置。它向用户隐藏了模块的内部依赖性,以避免意外使用任何传递性依赖性,从而加快了编译速度,并减少了重新编译的时间。

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

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

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

我创建了post,并且对Working Example: source code的每个人都有深入的了解

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

Gradle configurations

答案 9 :(得分:-2)

+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| 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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+