【问题标题】:What's the difference between implementation, api and compile in Gradle?Gradle 中的 implementation、api 和 compile 有什么区别?
【发布时间】:2017-11-13 13:43:56
【问题描述】:

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

例子:

 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'

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

【问题讨论】:

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


    【解决方案1】:

    tl;dr

    只需替换:

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

    Google announced at IO17Android Gradle plugin 3.0 带来的重大变化之一。

    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 配置提供的保证。


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

    【讨论】:

    • 谁是“消费者”?
    • 消费者是使用库的模块。在 Android 的情况下,它是 Android 应用程序。我认为这很清楚,我不确定这是否是您要求的。
    • 对我来说也是这样。但是,如果我正在创建一个库,我当然希望它的 API 能够暴露给应用程序。否则,应用程序开发人员将如何使用我的库?这就是为什么我不明白 implementation 隐藏依赖项的含义。我的问题有意义吗?
    • 是的,如果您的应用程序依赖于库 x,而库 x 本身又依赖于 y,z,那么这很有意义。如果你使用implementation只会暴露x api,但如果你使用api y,z 也会暴露。
    • 知道了!这现在更有意义了。您可以将此解释添加到您的答案中。它比引用的文档更清楚。
    【解决方案2】:

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


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

    • app(Android 应用)
    • myandroidlibrary(Android 库)
    • myjavalibrary(Java 库)

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

    myjavalibrary 有一个 MySecret

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

    myandroidlibrary 具有 MyAndroidComponent 类,可操纵来自 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,所以在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')           
    }
    

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

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

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

    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 上提供了此答案的示例项目

    【讨论】:

    • 我已经使用实现向一个 jar 文件添加了依赖项,如果它没有公开访问它为什么我仍然能够获得并且我的代码工作正常?
    • @smkrn110 实现将公开您的 jar 库,但不会公开您的 jar 依赖库。
    • @WijaySharma 接受的答案指出compile 不保证与api 保证的相同。
    • 我认为这应该是公认的答案。解释得很好!
    • @StevenW.Klassen 这是我听过的最不值得的反对票。如果您认为信息的顺序不是最佳的,建议修改而不是抱怨它
    【解决方案3】:

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

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

    简短的部分是-

    标准 Java 插件和 Java 的主要区别 库插件是后者引入了一个API的概念 暴露给消费者。库是一个 Java 组件 被其他组件消耗。这是一个非常常见的用例 多项目构建,但只要你有外部 依赖关系。

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

    更多解释请参考这张图片。

    【讨论】:

      【解决方案4】:

      简要解决方案:

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

      【讨论】:

      • 简洁明了的解释。谢谢!
      【解决方案5】:
      +--------------------+----------------------+-------------+--------------+-----------------------------------------+
      | 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.  |
      +--------------------+----------------------+-------------+--------------+-----------------------------------------+
      

      【讨论】:

      • 不直接回答问题
      • 还有一个developmentOnly
      • 如果我需要运行时和编译时间,我应该使用什么?目前,我有implementation,后跟runtime
      【解决方案6】:

      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 = publicimplementation = internalcompileOnly = private - 我需要为这些函数创建这样的别名,因为它们非常混乱。
      【解决方案7】:

      外行用语的简单区别是:

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

      阅读@aldok 的答案以获得全面的示例。

      【讨论】:

      • 但问题是,如果一个人故意来这里寻找这些问题的答案,那他毕竟不是门外汉。
      【解决方案8】:

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

      • 如果可能,首选implementation 配置而不是api

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

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

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

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

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

      【讨论】:

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

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

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

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

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

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

        【讨论】:

          【解决方案10】:

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

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


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

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

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

          如图所示,如果您希望一个库既可用于编译,又希望将其添加到构建文件中,则应使用implementation

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

          【讨论】:

            【解决方案11】:

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

            举个例子

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

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

            1 级

            使用 api 和 implementation,我们可以控制 level 3 的类是否应该暴露给 level 1。

            这如何加快构建速度:

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

            【讨论】:

              猜你喜欢
              • 2016-06-27
              • 2016-10-15
              • 1970-01-01
              • 2011-01-14
              • 1970-01-01
              相关资源
              最近更新 更多