繁体   English   中英

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

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

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

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

tl;博士

只需更换:

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

这是谷歌在 IO17 上宣布的Android 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 文件,这些文件可以准确地区分针对库进行编译所需的内容和在运行时使用库所需的内容(换句话说,不要混合编译库本身所需的内容和针对库进行编译所需的内容)。

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


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

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


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

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

appmyandroidlibrary作为依赖项。 myandroidlibrarymyjavalibrary作为依赖项。

依赖1

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中的值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')      
}

依赖2

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

我们有三个选择:

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

依赖3

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

编译或 Api(选项 #2 或 #3) 依赖4

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

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

实施(选项#1)

依赖5

如果您使用的是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 配置的要点,请参阅表 49.1。 Java 库插件 - 用于声明依赖项的配置以获得更详细的解释。

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

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

您可以在API 和实现分离部分阅读文档。

简短的部分是-

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

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

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

简要解决方案:

更好的方法是用implementation依赖替换所有compile依赖。 只有在泄漏模块接口的地方,才应该使用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博客提供

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

Gradle 依赖配置

Gradle 3.0引入了以下更改:

  • compile -> api

    api关键字与已弃用的compile相同,后者为所有级别公开此依赖项

  • compile -> implementation

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

  • provided -> compileOnly

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

  • compile -> annotationProcessor

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

  • apk -> runtimeOnly

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

【POM依赖类型】

外行术语的简要区别是:

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

阅读@aldok 的答案以获得一个全面的例子。

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

  • 如果可能,更喜欢implementation配置而不是api

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

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

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

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

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

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

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


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

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

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

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

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

摇篮

  • 实现:我们主要使用实现配置。 它将模块的内部依赖隐藏给它的使用者,以避免意外使用任何传递依赖,因此编译速度更快,重新编译更少。

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

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

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

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

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

Gradle 配置

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

让我们举个例子

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

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

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

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

在此处输入图片说明

这如何使构建更快:

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

其他答案解释了差异。

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

  • Groovy (build.gradle)
     Kotlin (build.gradle.kts)
     implementation("com.android.support:appcompat-v7:25.0.0") testImplementation("junit:junit:4.12")<\/code><\/pre><\/li><\/ul>"

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM