我正在尝试弄清楚构建我的依赖项时api
和implementation
配置之间的区别。
在文档中,它说implementation
有更好的构建时间,但是,
在一个类似的问题中看到这个comment,我想知道它是否属实
由于我不是gradle的专家,我希望有人可以提供帮助。我已经阅读了documentation,但我想知道一个易于理解的解释。
答案 0 :(得分:276)
Gradle compile
关键字已弃用,以支持api
和implementation
关键字来配置依赖关系。
使用api
相当于使用已弃用的compile
,因此如果您将所有compile
替换为api
,一切都会一如既往。
要了解implementation
关键字,请考虑以下示例。
示例强>
假设您有一个名为MyLibrary
的库,它在内部使用另一个名为InternalLibrary
的库。像这样:
// 'InternalLibrary' module
public class InternalLibrary {
public static String giveMeAString(){
return "hello";
}
}
// 'MyLibrary' module
public class MyLibrary {
public String myString(){
return InternalLibrary.giveMeAString();
}
}
假设MyLibrary
build.gradle
在api
中使用dependencies{}
配置,如下所示:
dependencies {
api project(':InternalLibrary')
}
您希望在代码中使用MyLibrary
,因此在您的应用build.gradle
中添加此依赖项:
dependencies {
api project(':MyLibrary')
}
使用api
配置(或已弃用的compile
),您可以在应用代码中访问MyLibrary
和InternalLibrary
:
// Access 'MyLibrary' (as desired and expected)
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());
通过这种方式,模块MyLibrary
可能会“泄漏”某些内部实现。您不应该(能够)使用它,因为它不是由您直接导入的。
引入了implementation
配置以防止这种情况发生。
现在,如果您在implementation
中使用api
而不是MyLibrary
:
dependencies {
implementation project(':InternalLibrary')
}
在你的应用中build.gradle
:
dependencies {
implementation project(':MyLibrary')
}
您将无法再在应用代码中拨打InternalLibrary.giveMeAString()
。
请注意,如果MyLibrary
使用api
导入InternalLibrary
,您的应用将能够毫无问题地致电InternalLibrary.giveMeAString()
,无需使用api
或implementation
将MyLibrary
添加到您的应用中。
这种拳击策略允许Android Gradle插件知道如果您在InternalLibrary
中编辑某些内容以仅触发MyLibrary
的重新编译而不会触发重新编译整个应用程序,因为您不知道可以访问InternalLibrary
。
当你有很多嵌套依赖项时,这种机制可以大大加快构建速度。 (观看最后链接的视频,以便全面了解这一点)
<强>结论强>
当您切换到新的Android Gradle插件3.X.X时,您应该用compile
关键字(1 *)替换所有implementation
。然后尝试编译和测试您的应用程序。如果一切正常,请保留代码,如果您遇到问题,可能是您的依赖项有问题,或者您使用了现在是私有且无法访问的内容。 Android Gradle插件工程师Jerome Dochez的建议(1 )* )
如果您是库管理员,则应使用api
作为库的公共API所需的每个依赖项,而使用implementation
作为测试依赖项或依赖项,而不是最终用户使用。
Useful article展示实施和 api
之间的区别<强>参考强> (这是为节省时间而拆分的视频)
Google I/O 2017 - How speed up Gradle builds (FULL VIDEO)
Google I/O 2017 - How speed up Gradle builds (NEW GRADLE PLUGIN 3.0.0 PART ONLY)
Google I/O 2017 - How speed up Gradle builds (reference to 1*)
答案 1 :(得分:64)
我喜欢将api
依赖关系视为公开(由其他模块看到),而implementation
依赖关系为私有(仅限于这个模块)。
请注意,与public
/ private
变量和方法不同,运行时不会强制执行api
/ implementation
个依赖项。这只是一个构建时优化,它允许Gradle
知道当其中一个依赖项更改其API时需要重新编译的模块。
答案 2 :(得分:5)
来自@matpag和@dev-bmax的答案非常清楚,足以使人们理解实现和api之间的不同用法。我只想从另一个角度做一个额外的解释,希望对有同样问题的人们有所帮助。
我创建了两个测试项目:
上面描述的依赖关系层次结构如下:
[project-b]-> [project-a]-> [spring-boot-gradle-plugin]
然后我测试了以下方案:
通过实现来使项目A取决于'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE'。
在终端B的根目录中运行gradle dependencies
命令,通过以下输出屏幕截图,我们可以看到'spring-boot-gradle-plugin'出现在runtimeClasspath依赖关系树中,而不是出现在compileClasspath的树中这就是为什么我们不能使用通过实现声明的库的原因,只是不能通过编译实现。
使项目A取决于 api
的'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE'再次在终端中输入B根目录,运行gradle dependencies
命令。
现在,“ spring-boot-gradle-plugin”同时出现在compileClasspath和runtimeClasspath依赖关系树中。
我注意到的一个重要区别是,以实现方式声明的生产者/库项目中的依赖项不会出现在使用者项目的compileClasspath中,因此我们不能在使用者项目中使用相应的lib。
答案 3 :(得分:3)
请考虑使用JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("document.getElementById('//id of element').setAttribute('value', '//value to set')");
// to click
js.executeScript("arguments[0].click();", element);
模块,该模块使用app
作为库,lib1
使用lib1
作为库。像这样的东西:lib2
。
现在在app -> lib1 -> lib2
中使用api lib2
时,则lib1
在使用app
或{{时可以看到 lib2
代码1}}。
但是在api lib1
中使用implementation lib1
时,则app
看不到 implementation lib2
代码。
答案 4 :(得分:3)
让我们看一下基于JVM的项目的非常简单的构建脚本。
plugins {
id 'java-library'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.hibernate:hibernate-core:3.6.7.Final'
api 'com.google.guava:guava:23.0'
testImplementation 'junit:junit:4.+'
}
实施
编译项目生产源所需的依赖关系,这些依赖关系不是项目公开的API的一部分。例如,该项目将Hibernate用于其内部持久层实现。
api
编译项目生产源所需的依赖关系,这些依赖关系是项目公开的API的一部分。例如,该项目使用Guava,并在其方法签名中公开带有Guava类的公共接口。
答案 5 :(得分:0)
现在documentation中有很好的解释
<块引用>api 配置应该用于声明依赖项,这些依赖项是 由库 API 导出,而实现配置 应该用于声明内部的依赖项 组件。