Gradle Implementation vs API配置

Vip*_*pha 183 implementation dependencies android gradle

我正在试图弄清楚构建我的依赖项时有什么区别apiimplementation配置.
在文档中,它说implementation有更好的构建时间,但是,在类似的问题中看到这个 评论我想知道它是否属实.
由于我不是gradle的专家,我希望有人可以提供帮助.我已经阅读了文档,但我想知道一个易于理解的解释.

Mat*_*Pag 337

compile不推荐使用Gradle 关键字,而使用apiimplementation关键字来配置依赖关系.

使用api是使用过时的等同compile,因此,如果您更换所有compileapi一切都将一如既往的作品.

要了解implementation关键字,请考虑以下示例.

假设您有一个名为的库MyLibrary,它在内部使用另一个名为的库InternalLibrary.像这样的东西:

    // 'InternalLibrary' module
    public class InternalLibrary {
        public static String giveMeAString(){
            return "hello";
        }
    }
Run Code Online (Sandbox Code Playgroud)
    // 'MyLibrary' module
    public class MyLibrary {
        public String myString(){
            return InternalLibrary.giveMeAString();
        }
    }
Run Code Online (Sandbox Code Playgroud)

假设MyLibrary build.gradle使用api配置dependencies{}如下:

dependencies {
    api project(':InternalLibrary')
}
Run Code Online (Sandbox Code Playgroud)

您希望MyLibrary在代码中使用,因此在您的应用中build.gradle添加此依赖项:

dependencies {
    implementation project(':MyLibrary')
}
Run Code Online (Sandbox Code Playgroud)

使用api配置(或已弃用compile),您可以访问InternalLibraryMyLibrary应用程序代码:

// Access 'MyLibrary' (granted)
MyLibrary myLib = new MyLibrary();
System.out.println(myLib.myString());

// Can ALSO access the internal library too (and you shouldn't)
System.out.println(InternalLibrary.giveMeAString());
Run Code Online (Sandbox Code Playgroud)

通过这种方式,该模块implementation可能会"泄漏"某些内部实现.您不应该(能够)使用它,因为它不是由您直接导入的.

implementation引入的配置,以防止这一点.所以,现在,如果你使用api的,而不是MyLibraryInternalLibrary.giveMeAString():

dependencies {
    implementation project(':InternalLibrary')
}
Run Code Online (Sandbox Code Playgroud)

在你的应用程序中InternalLibrary:

    // 'InternalLibrary' module
    public class InternalLibrary {
        public static String giveMeAString(){
            return "hello";
        }
    }
Run Code Online (Sandbox Code Playgroud)

您将无法再使用MyLibrary应用代码.

请注意,如果InternalLibrary用于compile导入implementation,您的应用能够api毫无问题地进行呼叫,而无需使用implementationcompile添加api到您的应用.

这种装箱策略允许Android Gradle插件知道如果您编辑某些内容implementationapi仅触发重新编译而不会触发整个应用程序的重新编译,因为您无权访问compile.

当你有很多嵌套依赖项时,这种机制可以大大加快构建速度.(观看最后链接的视频,以便全面了解这一点)

结论

  • 当您切换到新的Android插件摇篮3.XX,则应更换所有compileapi关键字(1*) .然后尝试编译和测试您的应用程序.如果一切正常,请保留代码,如果您遇到问题,可能是您的依赖项有问题,或者您使用了现在是私有且无法访问的内容.Android Gradle插件工程师Jerome Dochez的建议(1)*)

  • 如果您是库管理员,则应该使用库implementation的公共API所需的每个依赖项,同时MyLibrary用于最终用户不能使用的测试依赖项或依赖项.

有用的文章展示了实现api之间的区别

参考文献 (这是为节省时间而拆分的视频)

Google I/O 2017 - Gradle如何加速构建(完整视频)

Google I/O 2017 - Gradle如何加速构建(仅限新版PLADIN 3.0.0部分)

Google I/O 2017 - Gradle的构建速度(参考1*)

Android文档

  • 我认为答案是不准确的,应用程序可以使用MyLibrary所需的任何范围。它会根据MyLibrary是否使用api /实现来查看InternalLibrary。 (3认同)
  • 这是一个很好的解释。理论和具体完美地结合在一起。做得好。感谢那 (3认同)
  • 我注意到api在库模块中似乎无法正常运行。如果使用它,我仍然无法从我的应用程序项目中访问依赖项。我只能访问该库本身中的代码。 (2认同)
  • 这很好,适用于调试版本,但是当使用 ProGuard(在发布版本上)时,`MyLibrary#myString()` 会崩溃,因为 ProGuard 将删除 `InternalLibrary`。在 ProGuard 应用程序中使用 android-libs 的最佳实践是什么? (2认同)
  • 谢啦。很棒的解释,比android官方文档中给出的解释好得多 (2认同)

dev*_*max 86

我喜欢将api依赖关系视为公共(通过其他模块看到),而将implementation依赖关系视为私有(仅由此模块看到).

注意,与public/ privatevariables和方法不同,api/ implementationdependencies不是由运行时强制执行的.这只是一个构建时优化,它允许Gradle在其中一个依赖项更改其API时知道需要重新编译的模块.

  • 爱这个答案的简单性,非常感谢 (3认同)
  • 真正的区别(AFAICT)是生成的 pom 文件将 `api` 依赖项放在“编译”范围内(它们将作为依赖项包含在你的库和任何依赖你的库的东西中)和“实现”依赖项在“运行时”范围内(当您的代码运行时,它们最好在类路径上,但编译使用您的库的其他代码时不需要它们)。 (2认同)
  • 您应该对运行(以及编译您的库)所需的任何依赖项使用“实现”,但不应将其自动拉入使用您的库的项目中。一个例子是 jax-rs,您的库可能使用 RESTeasy,但它不应该将这些库拉到任何使用您的库的项目中,因为他们可能想使用 Jersey 代替。 (2认同)

小智 19

请参考链接:Android Studio 依赖配置,可在 android 开发者的官方网站上找到。

在依赖项块内,您可以使用几种不同的依赖项配置之一(例如上面显示的实现)来声明库依赖项。每个依赖项配置都为 Gradle 提供了有关如何使用依赖项的不同说明。

执行

Gradle 将依赖项添加到编译类路径并将依赖项打包到构建输出。但是,当您的模块配置实现依赖项时,它会让 Gradle 知道您不希望该模块在编译时将依赖项泄漏给其他模块。也就是说,依赖关系仅在运行时可供其他模块使用。使用此依赖项配置而不是 api 或编译(已弃用)可以显着缩短构建时间,因为它减少了构建系统需要重新编译的模块数量。例如,如果实现依赖项更改了其 API,Gradle 只会重新编译该依赖项以及直接依赖于它的模块。大多数应用程序和测试模块应使用此配置。

应用程序编程接口

Gradle 将依赖项添加到编译类路径和构建输出中。当模块包含 api 依赖项时,它会让 Gradle 知道该模块想要将该依赖项传递导出到其他模块,以便它们在运行时和编译时都可用。此配置的行为就像编译(现已弃用),但您应该谨慎使用它,并且仅与需要传递导出到其他上游使用者的依赖项一起使用。这是因为,如果 api 依赖项更改了其外部 API,Gradle 会在编译时重新编译有权访问该依赖项的所有模块。因此,拥有大量 api 依赖项会显着增加构建时间。除非您想将依赖项的 API 公开给单独的模块,否则库模块应该使用实现依赖项。


kol*_*bok 12

api关于vs的另一项技术说明implementation。假设您有以下依赖项:

dependencies {
  api "com.example:foo:1.0"
  implementation "com.example:bar:1.0"
}
Run Code Online (Sandbox Code Playgroud)

如果您在本地 Maven 存储库中安装生成的 jar 文件(借助maven-publish插件),您将看到生成的pom.xml文件如下所示:

    <dependency>
      <groupId>com.example</groupId>
      <artifactId>foo</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>bar</artifactId>
      <version>1.0</version>
      <scope>runtime</scope>
    </dependency>
Run Code Online (Sandbox Code Playgroud)

注意:api已转换为compile范围和implementation- 为runtime范围。

这允许该库的使用者避免在其编译类路径中存在运行时依赖项。


Cam*_*lva 11

来自gradle 文档

\n\n

让\xe2\x80\x99s 看一下基于 JVM 的项目的一个非常简单的构建脚本。

\n\n
plugins {\n    id \'java-library\'\n}\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \'org.hibernate:hibernate-core:3.6.7.Final\'\n    api \'com.google.guava:guava:23.0\'\n    testImplementation \'junit:junit:4.+\'\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

执行

\n\n

编译项目的生产源所需的依赖项不属于项目公开的 API 的一部分。例如,该项目使用 Hibernate 来实现其内部持久层。

\n\n

应用程序编程接口

\n\n

编译项目的生产源所需的依赖项是项目公开的 API 的一部分。例如,该项目使用 Guava 并在其方法签名中公开带有 Guava 类的公共接口。

\n
\n


Yvg*_*gen 6

现在文档中有很好的解释

api 配置应用于声明库 API 导出的依赖项,而实现配置应用于声明组件内部的依赖项。


Ehs*_*adi 5

想想你拥有app它采用模块lib1作为一个库和lib1使用lib2作为一个库。像这样的东西:app -> lib1 -> lib2

现在,在中使用api lib2lib1,然后在模块中使用:或时app 可以看到 lib2代码。api lib1implementation lib1app

但是使用的时候implementation lib2lib1,则app 无法看到lib2代码。


Ron*_*g.l 5

Answers from @matpag and @dev-bmax are clear enough to make people understand different usages between implementation and api. I just want to make an extra explaination from another angle, hopes to help for peoples that have the same question.

I created two projects for testing :

  • project A as a java library project named 'frameworks-web-gradle-plugin' depends on 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE'
  • project B depends on project A by implementation 'com.example.frameworks.gradle:frameworks-web-gradle-plugin:0.0.1-SNAPSHOT'

The dependencies hierarchy descripted above looks like:

[project-b] -> [project-a] -> [spring-boot-gradle-plugin]

Then I tested following scenarios:

  1. Make project A depends on 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE' by implementation .

    Run gradle dependencies command in a terminal in poject B root dir?with following screenshot of output we can see that 'spring-boot-gradle-plugin' appears in runtimeClasspath dependencies tree, but not in compileClasspath's, I think that's exactly why we can't make use of library that declared using implementation, it just won't through compilation.

    在此处输入图片说明

  2. Make project A depends on 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE' by api

    Run gradle dependencies command in a terminal in poject B root dir again. Now 'spring-boot-gradle-plugin' appears both in compileClasspath and runtimeClasspath dependencies tree.

    在此处输入图片说明

A significant difference I noticed is that the dependency in producer/library project declared in implementation way won't appear in compileClasspath of consumer projects, so that we can't make use of corresponding lib in the consumer projects.