在 Gradle 中实现和编译有什么区别?

在更新到 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'

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

答案

tl; dr

只需替换:

  • 使用implementation (如果不需要传递性)或api (如果需要传递性)进行compile
  • 使用testImplementation testCompile
  • debugCompile进行debugImplementation
  • androidTestCompileandroidTestImplementation
  • compileOnly仍然有效。在 3.0 中添加了它以替换提供的内容,而不进行编译。 ( provided推出的时候摇篮没有为用例配置名称后 Maven 的提供范围命名它。)

这是 Google 在 IO17 上宣布的 Gradle 3.0 带来的重大变化之一

compile配置现已弃用 ,应由implementationapi代替

Gradle 文档中

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

api配置中出现的依赖项将传递给库的使用者,并因此出现在使用者的编译类路径上。

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

  • 依赖项不会再泄漏到使用者的编译类路径中,因此您永远不会意外地依赖于传递性依赖项
  • 减少类路径大小,加快了编译速度
  • 实施依赖项发生更改时,重新编译次数更少:无需重新编译使用者
  • 更清洁的发布:与新的 maven-publish 插件一起使用时,Java 库生成的 POM 文件可以准确区分针对该库进行编译所需的内容和在运行时使用该库所需的内容(换句话说,不要混合编译库本身所需的内容和对库进行编译所需的内容)。

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


注意:如果您仅在应用程序模块中使用库(常见情况),则不会有任何区别。
仅当您有一个包含彼此依赖的模块的复杂项目时,或者正在创建一个库时,您才会看到差异。

public class MySecret {

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

    private static String component = MySecret.getSecret();

    public static String getComponent() {
        return "My component: " + component;
    }    
}
TextView tvHelloWorld = findViewById(R.id.tv_hello_world);
tvHelloWorld.setText(MyAndroidComponent.getComponent());
dependencies {
    implementation project(':myandroidlibrary')      
}
dependencies {
    // Option #1
    implementation project(':myjavalibrary') 
    // Option #2
    compile project(':myjavalibrary')      
    // Option #3
    api project(':myjavalibrary')           
}
TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can access MySecret
textView.setText(MySecret.getSecret());
TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can NOT access MySecret
textView.setText(MySecret.getSecret()); // Won't even compile

不建议使用Compile配置,应将其替换为implementationapi

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

简短的部分是 -

标准 Java 插件和 Java 库插件之间的主要区别在于,后者引入了向消费者公开的 API 的概念。库是一个 Java 组件,打算由其他组件使用。在多项目构建中,这是一个非常常见的用例,但在您具有外部依赖关系时也是如此。

该插件提供了两个可用于声明依赖关系的配置:api 和实现。 api 配置应用于声明由库 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'
         })
 }
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| 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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+

外行用语的简要区别是:

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

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

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

  • 尽可能将实现配置优先于 api

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

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

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

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

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