我试图弄清楚在构建依赖关系时api和实现配置之间的区别。

在文档中,它说实现有更好的构建时间,但是,在一个类似的问题中看到这个评论,我想知道这是否是真的。

因为我不是Gradle的专家,我希望有人能帮助我。我已经阅读了文档,但我想知道一个简单易懂的解释。


当前回答

我喜欢把api依赖看作是公共的(其他模块可以看到),而实现依赖则是私有的(只有这个模块可以看到)。

注意,与公共/私有变量和方法不同,api/实现依赖关系不是由运行时强制执行的。这只是一个构建时优化,它允许Gradle知道当某个依赖项更改API时需要重新编译哪些模块。

其他回答

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时需要重新编译哪些模块。

假设你有一个应用模块,它使用lib1作为库,lib1使用lib2作为库。就像这样:app -> lib1 -> lib2。

现在当在lib1中使用api lib2时,当在app模块中使用api lib1或实现lib1时,app可以看到lib2代码。

但是当在lib1中使用实现lib2时,应用程序无法看到lib2代码。

@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文档:

让我们看一下基于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类的公共接口。