首页 文章

Gradle中的实现和编译之间有什么区别?

提问于
浏览
617

在更新到Android Studio 3.0并创建一个新项目之后,我注意到在 build.gradle 中有一种新方法来添加新的依赖项而不是 compileimplementation 而不是 testCompiletestImplementation .

例:

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'

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

5 回答

  • 162

    The brief difference in layman's term is:

    • 如果您正在使用通过公开所述依赖项的成员来为其他模块提供支持的接口或模块,那么您应该使用'api' .

    • 如果要创建要在内部实现或使用所述依赖项的应用程序或模块,请使用'implementation' .

    • 'compile'与'api'的工作方式相同,但是,如果您只是实现或使用任何库,'implementation'将更好地工作并节省您的资源 .

    阅读@aldok的答案以获得一个全面的例子 .

  • 36

    Brief Solution:

    更好的方法是用 implementation 依赖项替换所有 compile 依赖项 . 只有泄漏模块接口的地方,才应使用 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'
             })
     }
    

    Explain More:

    Before Android Gradle plugin 3.0 :我们遇到了一个很大的问题,即一个代码更改导致所有模块被重新编译 . 造成这种情况的根本原因是Gradle不知道您是否通过另一个模块泄漏模块的接口 .

    After Android Gradle plugin 3.0 :最新的Android Gradle插件现在要求您明确定义是否泄漏模块的界面 . 基于此,它可以在应该重新编译的内容上做出正确的选择 .

    因此, compile 依赖项已被弃用,取而代之的是两个新的依赖项:

    • api :您通过自己的界面泄漏了此模块的接口,这意味着与旧的 compile 依赖项完全相同

    • implementation :您只在内部使用此模块,不会通过您的界面泄漏它

    因此,如果已使用模块的接口发生变化,您现在可以明确告诉Gradle重新编译模块 .

    礼貌Jeroen Mols博客

  • 837

    Compile 配置已弃用,应替换为 implementationapi .

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

    简介部分是 -

    标准Java插件和Java Library插件之间的主要区别在于后者引入了向消费者公开的API的概念 . 库是一个旨在被其他组件使用的Java组件 . 这是多项目构建中非常常见的用例,但只要您有外部依赖项 . 该插件公开了两种可用于声明依赖关系的配置:api和实现 . api配置应该用于声明由库API导出的依赖项,而实现配置应该用于声明组件内部的依赖项 .

    有关进一步说明,请参阅此图像 .
    Brief explanation

  • 46

    这个答案将展示项目中 implementationapicompile 之间的区别 . 假设我有一个包含三个Gradle模块的项目:

    • app(Android应用程序)

    • myandroidlibrary(Android库)

    • myjavalibrary(一个Java库)

    appmyandroidlibrary 作为依赖项 . myandroidlibrarymyjavalibrary 作为依赖项 .

    app - > myandroidlibrary - > myjavalibrary

    myjavalibrary 有一个 MySecret

    public class MySecret {
    
        public static String getSecret() {
            return "Money";
        }
    }
    

    myandroidlibraryMyAndroidComponent 类操纵 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 build.gradle的依赖关系 . 它非常简单直观 .

    dependencies {
        implementation project(':myandroidlibrary')      
    }
    

    您认为 myandroidlibrary build.gradle会是什么样子?我们有三种选择:

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

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

    Compile and Api

    如果你正在使用 compileapi . 我们的Android应用程序现在能够访问 myandroidcomponent 依赖项,这是一个 MySecret 类 .

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

    Implementation

    如果您正在使用 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配置的要点,有关详细说明,请参阅Table 49.1. Java Library plugin - configurations used to declare dependencies .

    有关此答案的示例项目,请访问https://github.com/aldoKelvianto/ImplementationVsCompile

  • 2

    tl;dr

    只需更换:

    • compileimplementation

    • testCompiletestImplementation

    • debugCompiledebugImplementation

    • androidTestCompileandroidTestImplementation

    • compileOnly 仍然有效 . 它在3.0中添加以替换提供而不是编译 . (_367872_在Gradle没有't have a configuration name for that use-case and named it after Maven'提供范围时介绍 . )

    这是Gradle 3.0即将发生的重大变化之一Google announced at IO17 .

    compile 配置为now deprecated,应替换为 implementationapi

    来自Gradle documentation

    依赖{
    api'commons-httpclient:commons-httpclient:3.1'
    实现'org.apache.commons:commons-lang3:3.5'
    }
    出现在api配置中的依赖关系将传递给库的消费者,因此将出现在消费者的编译类路径中 . 另一方面,实现配置中的依赖关系不会暴露给消费者,因此不会泄漏到消费者的编译类路径中 . 这带来了几个好处:依赖性不再泄漏到消费者的编译类路径中,因此,当实现依赖性发生变化时,由于减少了类路径大小而减少了重新编译,因此您永远不会意外地依赖于传递依赖性更快的编译:消费者不需要重新编译更清洁发布:当与新的maven-publish插件结合使用时,Java库生成POM文件,这些文件准确区分编译库所需的内容和在运行时使用库所需的内容(换句话说,不要混合编译库本身需要什么以及编译库需要什么 . 编译配置仍然存在,但不应使用,因为它不提供api和实现配置提供的保证 .


    Note: 如果您只在应用程序模块中使用库 - 常见情况 - 您将不会注意到任何差异 .
    如果您有一个复杂的项目,模块相互依赖,或者您正在创建一个库,那么您将只能看到差异 .

相关问题