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

What's the difference between implementation, api and compile in Gradle?

更新到 Android Studio 3.0 并创建新项目后,我注意到 build.gradle 中有一种添加新依赖项的新方法,而不是 compile implementation 而不是 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'

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

tl;dr

只需替换:

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

这是 Android Gradle plugin 3.0 that 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'
}

Dependencies appearing in the api configurations will be transitively exposed to consumers of the library, and as such will appear on the compile classpath of consumers.

Dependencies found in the implementation configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers' compile classpath. This comes with several benefits:

  • dependencies do not leak into the compile classpath of consumers anymore, so you will never accidentally depend on a transitive dependency
  • faster compilation thanks to reduced classpath size
  • less recompilations when implementation dependencies change: consumers would not need to be recompiled
  • cleaner publishing: when used in conjunction with the new maven-publish plugin, Java libraries produce POM files that distinguish exactly between what is required to compile against the library and what is required to use the library at runtime (in other words, don't mix what is needed to compile the library itself and what is needed to compile against the library).

The compile configuration still exists, but should not be used as it will not offer the guarantees that the api and implementation configurations provide.


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

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

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

简短的部分是-

The key difference between the standard Java plugin and the Java Library plugin is that the latter introduces the concept of an API exposed to consumers. A library is a Java component meant to be consumed by other components. It's a very common use case in multi-project builds, but also as soon as you have external dependencies.

The plugin exposes two configurations that can be used to declare dependencies: api and implementation. The api configuration should be used to declare dependencies which are exported by the library API, whereas the implementation configuration should be used to declare dependencies which are internal to the component.

如需进一步说明,请参阅此图片。

简要解决方案:

更好的方法是将所有 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 博客

此答案将展示 implementationapicompile 在项目上的区别。


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

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

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

myjavalibrary 有一个 MySecret class

public class MySecret {

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

myandroidlibraryMyAndroidComponent class 操纵来自 MySecret class.

的值
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,所以在appbuild.gradle中使用implementation.

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

dependencies {
    implementation project(':myandroidlibrary')      
}

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

我们有三种选择:

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

What's the difference between them and what should I be using?

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

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

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

实施(选项 #1)

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

上找到

通俗的说就是:

  • 如果您正在开发一个接口或模块,该接口或模块通过公开指定依赖项的成员来为其他模块提供支持,您应该使用 'api'。
  • 如果您制作的应用程序或模块将在内部实现或使用规定的依赖项,请使用 'implementation'。
  • 'compile' 与 'api' 的工作方式相同,但是,如果您只是实施或使用任何库,'implementation' 会更好地工作并节省资源。

阅读@aldok 的回答以获得综合示例。

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

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

  • Prefer the implementation configuration over api when possible

This keeps the dependencies off of the consumer’s compilation classpath. In addition, the consumers will immediately fail to compile if any implementation types accidentally leak into the public API.

So when should you use the api configuration? An API dependency is one that contains at least one type that is exposed in the library binary interface, often referred to as its ABI (Application Binary Interface). This includes, but is not limited to:

  • types used in super classes or interfaces
  • types used in public method parameters, including generic parameter types (where public is something that is visible to compilers. I.e. , public, protected and package private members in the Java world)
  • types used in public fields
  • public annotation types

By contrast, any type that is used in the following list is irrelevant to the ABI, and therefore should be declared as an implementation dependency:

  • types exclusively used in method bodies
  • types exclusively used in private members
  • types exclusively found in internal classes (future versions of Gradle will let you declare which packages belong to the public API)

Gradle依赖配置

Gradle 3.0 引入了下一个变化:

  • compile -> api

    api 关键字与 deprecated compile 相同,后者公开了所有级别的依赖关系

  • compile -> implementation

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

  • provided -> compileOnly

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

  • compile -> annotationProcessor

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

  • apk -> runtimeOnly

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

[POM dependency type]

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

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

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

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

我创建了一个post with an in-depth understanding of each one with Working Example: source code

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

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

The compile configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 7.0. Please use the implementation configuration instead.


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

所以你有蓝框 compileClasspathruntimeClassPath
compileClasspath 是在 运行 宁 gradle build 时成功构建所需的条件。编译时将出现在类路径中的库将是使用 compileOnlyimplementation.

在 gradle 构建中配置的所有库

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

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

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

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

举个例子

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

level 1使用level 2.level 2使用level 3.

级别 1 <- 级别 2 <- 级别 3

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

这如何使构建速度更快:

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

其他答案解释了差异。

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

  • Groovy (build.gradle)
    implementation 'com.android.support:appcompat-v7:25.0.0'
    testImplementation 'junit:junit:4.12'
    
  • 科特林 (build.gradle.kts)
    implementation("com.android.support:appcompat-v7:25.0.0")
    testImplementation("junit:junit:4.12")