在Gradle中实现和编译有什么区别?


1024

更新到Android 3.0工作室并创建一个新项目后,我注意到,在build.gradle没有增加新的依赖关系,而不是一种新的方式compile出现implementation的,而不是和testCompile存在testImplementation

例:

 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'

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

Answers:


1276

tl; dr

只需替换:

  • compileimplementation(如果您不需要传递性)或api(如果您需要传递性)
  • testCompiletestImplementation
  • debugCompiledebugImplementation
  • androidTestCompileandroidTestImplementation
  • compileOnly仍然有效。在3.0中添加了它以替换提供的内容,而不进行编译。(provided在Gradle没有该用例的配置名称并以Maven提供的作用域命名时使用。)

这是Google 在IO17上宣布的 Gradle 3.0带来的重大变化之一

现在已弃用compile配置,应将其替换为或implementationapi

Gradle文档中

dependencies {
    api 'commons-httpclient:commons-httpclient:3.1'
    implementation 'org.apache.commons:commons-lang3:3.5'
}

api配置中出现的依赖项将传递给库的使用者,并因此出现在使用者的编译类路径上。

implementation另一方面,在配置中找到的依赖项不会暴露给使用者,因此不会泄漏到使用者的编译类路径中。这有几个好处:

  • 依赖项不会再泄漏到使用者的编译类路径中,因此您永远不会意外地依赖于传递性依赖项
  • 减少类路径大小,加快了编译速度
  • 实施依赖项发生更改时,重新编译次数更少:无需重新编译使用者
  • 更清洁的发布:与新的maven-publish插件一起使用时,Java库生成的POM文件可准确区分针对该库进行编译所需的内容和在运行时使用该库所需的内容(换句话说,不要混合编译库本身所需的内容和对库进行编译所需的内容)。

编译配置仍然存在,但不应使用,因为它不能提供apiimplementation配置所提供的保证。


注意:如果您仅在应用模块中使用库(常见情况),则不会有任何区别。
仅当您有一个包含彼此依赖的模块的复杂项目时,或者正在创建一个库时,您才会看到差异。


137
谁是“消费者”?
Suragch

34
使用者是使用该库的模块。如果是Android,则是Android应用程序。我认为这很清楚,我不确定这是否是您要的。
2015年

21
这对我来说也是如此。但是,如果要制作一个库,我当然希望其API对应用程序公开。否则,应用程序开发人员将如何使用我的库?这就是为什么我没有implementation隐藏依赖项的含义。我的问题有意义吗?
Suragch

234
是的,如果您的应用程序依赖于库x,而库x本身又依赖于y,z,那么这很有意义。如果implementation仅使用x api,则将被公开,但是,如果您使用apiy,z也将被公开。
2015年

36
得到它了!现在这更有意义。您可以将此解释添加到答案中。比引用的文档更清楚。
Suragch

378

这个答案将展示之间的差异implementationapi以及compile在一个项目。


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

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

app具有myandroidlibrary与依赖。myandroidlibrary具有myjavalibrary 与依赖。

依赖1

myjavalibraryMySecret

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')      
}

依赖2

您认为myandroidlibrarybuild.gradle应该是什么样?我们应该使用哪个范围?

我们有三种选择:

dependencies {
    // Option #1
    implementation project(':myjavalibrary') 
    // Option #2
    compile project(':myjavalibrary')      
    // Option #3
    api project(':myjavalibrary')           
}

依赖3

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

编译或Api(选项#2或#3) 依赖4

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

TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can access MySecret
textView.setText(MySecret.getSecret());

实施(选项1)

依赖5

如果您使用的是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配置的要点,请参阅表49.1。Java库插件-用于声明依赖关系的配置,以进行更详细的说明。

可在https://github.com/aldoKelvianto/ImplementationVsCompile上找到此答案的示例项目。


1
我已使用实现将依赖项添加到一个jar文件中,如果它没有公开访问它,为什么我仍然可以获取并且我的代码运行正常?
smkrn110 '18

@ smkrn110实现将公开您的jar库,但不会公开您的jar依赖库。
aldok '18 -4-3

2
@WijaySharma接受的答案表示compile不保证所保证的相同api
Sub 6资源

9
我认为这应该是公认的答案。好解释!
Shashank Kapsime

9
@ StevenW.Klassen是我听过的最不值得的唐纳德。如果您认为信息的顺序不是最佳的,则建议您进行编辑,而不要抱怨它
蒂姆

65

Compile不推荐使用此配置,应使用implementation或替换api

您可以在https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation中阅读文档。

简短的部分是-

标准Java插件和Java库插件之间的主要区别在于,后者引入了向消费者公开的API的概念。库是一个Java组件,打算由其他组件使用。在多项目构建中,这是一个非常常见的用例,但在您具有外部依赖关系时也是如此。

该插件提供了两个可用于声明依赖关系的配置:api和实现。api配置应用于声明由库API导出的依赖关系,而实现配置应用于声明组件内部的依赖关系。

有关更多说明,请参考此图像。 简要说明


46

简要解决方案:

更好的方法是compileimplementation依赖关系替换所有依赖关系。而且,只有在泄漏模块接口的地方,才应使用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博客提供


2
简洁明了的解释。谢谢!
LeOn-韩立

20
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| 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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+

不直接回答问题
skryvets 19-4-20

1
只有一个发展
Hohenheimsenberg

如果我既需要运行时又需要编译时,应该使用什么?目前,我implementation后面跟着一个runtime
Maroun

8

外行用语的简要区别是:

  • 如果您正在开发通过公开声明的依赖项的成员为其他模块提供支持的接口或模块,则应使用“ api”。
  • 如果您要制作的应用程序或模块将在内部实现或使用声明的依赖项,请使用“实现”。
  • 'compile'与'api'的工作原理相同,但是,如果您仅实现或使用任何库,则'implementation'会更好地工作并节省资源。

阅读@aldok的答案以获取完整示例。


但问题是,如果一个人故意来这里寻找这些问题的答案,那么他毕竟不是外行。
里沙夫

6

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

  • 尽可能优先使用implementation配置api

这使依赖项脱离使用者的编译类路径。此外,如果任何实现类型意外泄漏到公共API中,使用者将立即无法编译。

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

  • 超类或接口中使用的类型
  • 公共方法参数中使用的类型,包括通用参数类型(其中public是编译器可见的东西。即Java世界中的public,protected和package private成员)
  • 公共领域中使用的类型
  • 公开注释类型

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

  • 方法主体中专门使用的类型
  • 专用于私人会员的类型
  • 内部类中独有的类型(将来的Gradle版本将允许您声明哪些包属于公共API)

6

Gradle 3.0 介绍了下一个更改:

  • compile -> api

    api 关键字与已弃用的关键字相同 compile

  • compile -> implementation

    最好的方式,因为有一定的优势。implementation仅公开一个级别的依赖在构建时的依赖关系(该依赖关系在运行时可用)。结果,您的构建速度更快(无需重新编译高于1级的使用者)

  • provided -> compileOnly

    此依赖关系仅在编译时可用(该依赖关系在运行时不可用)。此依赖项不能是可传递的,而是.aar。它可以与编译时注释处理器一起使用,并允许您减少最终输出文件

  • compile -> annotationProcessor

    非常类似于,compileOnly但也保证对消费者不可见传递依赖

  • apk -> runtimeOnly

    依赖关系在编译时不可用,但在运行时可用。


那么也就是说,api = publicimplementation = internalcompileOnly = private-我需要为这些功能创建这样的别名,因为它们是超级混乱。
t3chb0t
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.