我试图弄清楚在构建依赖关系时api和实现配置之间的区别。
在文档中,它说实现有更好的构建时间,但是,在一个类似的问题中看到这个评论,我想知道这是否是真的。
因为我不是Gradle的专家,我希望有人能帮助我。我已经阅读了文档,但我想知道一个简单易懂的解释。
我试图弄清楚在构建依赖关系时api和实现配置之间的区别。
在文档中,它说实现有更好的构建时间,但是,在一个类似的问题中看到这个评论,我想知道这是否是真的。
因为我不是Gradle的专家,我希望有人能帮助我。我已经阅读了文档,但我想知道一个简单易懂的解释。
当前回答
请参考链接:Android Studio依赖配置可在Android开发者的官方网站。
在dependencies块中,您可以使用几种不同的依赖项配置(如上面所示的实现)之一来声明一个库依赖项。每个依赖项配置都为Gradle提供了关于如何使用依赖项的不同说明。
实现
Gradle adds the dependency to the compile classpath and packages the dependency to the build output. However, when your module configures an implementation dependency, it's letting Gradle know that you do not want the module to leak the dependency to other modules at compile time. That is, the dependency is available to other modules only at runtime. Using this dependency configuration instead of api or compile (deprecated) can result in significant build time improvements because it reduces the number of modules that the build system needs to recompile. For example, if an implementation dependency changes its API, Gradle recompiles only that dependency and the modules that directly depend on it. Most app and test modules should use this configuration.
api
Gradle adds the dependency to the compile classpath and build output. When a module includes an api dependency, it's letting Gradle know that the module wants to transitively export that dependency to other modules, so that it's available to them at both runtime and compile time. This configuration behaves just like compile (which is now deprecated), but you should use it with caution and only with dependencies that you need to transitively export to other upstream consumers. That's because, if an api dependency changes its external API, Gradle recompiles all modules that have access to that dependency at compile time. So, having a large number of api dependencies can significantly increase build time. Unless you want to expose a dependency's API to a separate module, library modules should instead use implementation dependencies.
其他回答
关于api和实现还有一个技术注意事项。假设你有以下依赖项:
dependencies {
api "com.example:foo:1.0"
implementation "com.example:bar:1.0"
}
如果你在本地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>
注意:api被转换为编译作用域和实现到运行时作用域。
这允许这个库的使用者避免在他们的编译类路径中有运行时依赖项。
@matpag和@dev-bmax的回答足够清楚,让人们理解实现和api之间的不同用法。我只是想从另一个角度做一个额外的解释,希望对有同样问题的人有所帮助。
我创建了两个项目进行测试:
项目A作为一个名为“frameworks-web-gradle-plugin”的java库项目依赖于“org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE” 项目B依赖于项目A的实现'com.example.frameworks.gradle:frameworks-web-gradle-plugin:0.0.1-SNAPSHOT'
上面描述的依赖层次结构如下:
[project-b] -> [project-a] -> [spring-boot-gradle-plugin]
然后我测试了以下场景:
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. 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.
我注意到一个显著的区别是,在生产者/库项目中以实现方式声明的依赖项不会出现在消费者项目的compileClasspath中,因此我们不能在消费者项目中使用相应的库。
Gradle的compile关键字已弃用,转而使用api和实现关键字来配置依赖项。
使用api相当于使用已弃用的compile,所以如果你将所有compile替换为api,一切都将一如既往地工作。
要理解实现关键字,请考虑以下示例。
例子
假设您有一个名为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。Gradle在依赖{}中使用API配置,如下所示:
dependencies {
api(project(":InternalLibrary"))
}
你想在你的代码中使用MyLibrary,所以在你的应用构建中。Gradle你添加这个依赖:
dependencies {
implementation(project(":MyLibrary"))
}
使用api配置(或已弃用的编译),您可以在应用程序代码中访问InternalLibrary:
// Access 'MyLibrary' (granted)
MyLibrary myLib = new MyLibrary();
System.out.println(myLib.myString());
// Can ALSO access the internal library too (but you shouldn't)
System.out.println(InternalLibrary.giveMeAString());
通过这种方式,模块MyLibrary可能会“泄露”某些东西的内部实现。你不应该(不能)使用它,因为它不是你直接导入的。
为了防止这种情况,引入了实现配置。 现在如果你在MyLibrary中使用implementation而不是api:
dependencies {
implementation(project(":InternalLibrary"))
}
你将不能在你的应用程序代码中调用internallibrary . givemeasstring()。
这种装箱策略允许Android Gradle插件知道,如果你在InternalLibrary中编辑一些东西,它必须只触发MyLibrary的重新编译,而不是重新编译你的整个应用程序,因为你没有访问InternalLibrary。
当你有很多嵌套依赖时,这种机制可以大大加快构建速度。(请观看最后链接的视频,以充分了解这一点)
结论
When you switch to the new Android Gradle plugin 3.X.X, you should replace all your compile with the implementation keyword *(1). Then try to compile and test your app. If everything it's ok leave the code as is, if you have problems you probably have something wrong with your dependencies or you used something that now is private and not more accessible. *Suggestion by Android Gradle plugin engineer Jerome Dochez (1)) If you are a library mantainer you should use api for every dependency which is needed for the public API of your library, while use implementation for test dependencies or dependencies which must not be used by the final users.
展示实现和api之间的区别的有用文章
参考文献 (为了节省时间,这是同一段视频)
谷歌I/O 2017 -如何加速Gradle构建(完整视频)
谷歌I/O 2017 -如何加速Gradle构建(新的Gradle插件3.0.0部分仅限)
谷歌I/O 2017 -如何加速Gradle构建(参考1*)
安卓系统文档
我喜欢把api依赖看作是公共的(其他模块可以看到),而实现依赖则是私有的(只有这个模块可以看到)。
注意,与公共/私有变量和方法不同,api/实现依赖关系不是由运行时强制执行的。这只是一个构建时优化,它允许Gradle知道当某个依赖项更改API时需要重新编译哪些模块。
文档中有很好的解释
api配置应该用于声明依赖关系 由库API导出,而实现配置 应用于声明依赖项,这些依赖项是 组件。