What's the difference between implementation a

2018-12-31 08:42发布

After updating to Android Studio 3.0 and creating a new project, I noticed that in build.gradle there is a new way to add new dependencies instead of compile there is implementation and instead of testCompile there is testImplementation.

Example:

 implementation 'com.android.support:appcompat-v7:25.0.0'
 testImplementation 'junit:junit:4.12'

instead of

 compile 'com.android.support:appcompat-v7:25.0.0'
 testCompile 'junit:junit:4.12'

What's the difference between them and what should I be using?

5条回答
几人难应
2楼-- · 2018-12-31 09:02

The brief difference in layman's term is:

  • If you are working on an interface or module that provides support to other modules by exposing the members of the stated dependency you should be using 'api'.
  • If you are making an application or module that is going to implement or use the stated dependency internally, use 'implementation'.
  • 'compile' worked same as 'api', however, if you are only implementing or using any library, 'implementation' will work better and save you resources.

read the answer by @aldok for a comprehensive example.

查看更多
十年一品温如言
3楼-- · 2018-12-31 09:04

This answer will demonstrate the difference between implementation, api, and compile on a project. Let's say I have a project with three Gradle modules:

  • app (an Android application)
  • myandroidlibrary (an Android library)
  • myjavalibrary (a Java library)

app has myandroidlibrary as dependencies. myandroidlibrary has myjavalibrary as dependencies.

app -> myandroidlibrary -> myjavalibrary

myjavalibrary has a MySecret class

public class MySecret {

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

myandroidlibrary has MyAndroidComponent class that manipulate value from MySecret class.

public class MyAndroidComponent {

    private static String component = MySecret.getSecret();

    public static String getComponent() {
        return "My component: " + component;
    }    
}

Lastly, app is only interested in the value from myandroidlibrary

TextView tvHelloWorld = findViewById(R.id.tv_hello_world);
tvHelloWorld.setText(MyAndroidComponent.getComponent());

Now, let's talk about dependencies on app build.gradle. It's simple and intuitive. app need to use :myandroidlibrary

dependencies {
    implementation project(':myandroidlibrary')      
}

But, what do you think myandroidlibrary build.gradle should look like? Which scope we should use?

We have three options:

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

What's the difference between them and what should I be using?

Compile and Api

If you're using compile and api. Our Android Application now able to access myandroidcomponent dependency, which is a MySecret class.

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

Implementation

If you're using implementation configuration, MySecret is not exposed.

TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can NOT access MySecret
textView.setText(MySecret.getSecret()); // Won't even compile

So, which configuration you should choose? That really depends on your requirement.

If you want to expose dependencies use api or compile, if you don't want to expose dependencies (hiding your internal module) then use implementation.

This is just a gist of Gradle configurations, refer to Table 49.1. Java Library plugin - configurations used to declare dependencies for more detailed explanation.

The sample project for this answer is available on https://github.com/aldoKelvianto/ImplementationVsCompile

查看更多
泪湿衣
4楼-- · 2018-12-31 09:12

tl;dr

Just replace:

  • compile with implementation
  • testCompile with testImplementation
  • debugCompile with debugImplementation
  • androidTestCompile with androidTestImplementation
  • compileOnly is still valid. It was added in 3.0 to replace provided and not compile. (provided introduced when Gradle didn't have a configuration name for that use-case and named it after Maven's provided scope.)

It is one of the breaking changes coming with Gradle 3.0 that Google announced at IO17.

The compile configuration is now deprecated and should be replaced by implementation or api

From the Gradle documentation:

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

Dependencies appearing in the api configurations will be transitively exposed to consumers of the library, and as such will appear on the compile classpath of consumers.

Dependencies found in the implementation configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers' compile classpath. This comes with several benefits:

  • dependencies do not leak into the compile classpath of consumers anymore, so you will never accidentally depend on a transitive dependency
  • faster compilation thanks to reduced classpath size
  • less recompilations when implementation dependencies change: consumers would not need to be recompiled
  • cleaner publishing: when used in conjunction with the new maven-publish plugin, Java libraries produce POM files that distinguish exactly between what is required to compile against the library and what is required to use the library at runtime (in other words, don't mix what is needed to compile the library itself and what is needed to compile against the library).

The compile configuration still exists, but should not be used as it will not offer the guarantees that the api and implementation configurations provide.


Note: if you are only using a library in your app module -the common case- you won't notice any difference.
you will only see the difference if you have a complex project with modules depending on each other, or you are creating a library.

查看更多
情到深处是孤独
5楼-- · 2018-12-31 09:16

Compile configuration was deprecated and should be replaced by implementation or api.

You can read the docs at https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation.

The brief part being-

The key difference between the standard Java plugin and the Java Library plugin is that the latter introduces the concept of an API exposed to consumers. A library is a Java component meant to be consumed by other components. It's a very common use case in multi-project builds, but also as soon as you have external dependencies.

The plugin exposes two configurations that can be used to declare dependencies: api and implementation. The api configuration should be used to declare dependencies which are exported by the library API, whereas the implementation configuration should be used to declare dependencies which are internal to the component.

For further explanation refer to this image. Brief explanation

查看更多
墨雨无痕
6楼-- · 2018-12-31 09:23

Brief Solution:

The better approach is to replace all compile dependencies with implementation dependencies. And only where you leak a module’s interface, you should use api. That should cause a lot less recompilation.

 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: we had a big problem which is one code change causes all modules to be recompiled. The root cause for this is that Gradle doesn’t know if you leak the interface of a module through another one or not.

After Android Gradle plugin 3.0: the latest Android Gradle plugin now requires you to explicitly define if you leak a module’s interface. Based on that it can make the right choice on what it should recompile.

As such the compile dependency has been deprecated and replaced by two new ones:

  • api: you leak the interface of this module through your own interface, meaning exactly the same as the old compile dependency

  • implementation: you only use this module internally and does not leak it through your interface

So now you can explicitly tell Gradle to recompile a module if the interface of a used module changes or not.

Courtesy of Jeroen Mols blog

查看更多
登录 后发表回答