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?
tl;dr
Just replace:
compile with implementation (if you don't need transitivity) or api (if you need transitivity)
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 Android Gradle plugin 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.
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.
https://i.stack.imgur.com/GDJlK.png
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...
app
need to consume :myandroidlibrary
, so in app
build.gradle use implementation
.
(Note: You can use api/compile too. But hold that thought for a moment.)
dependencies {
implementation project(':myandroidlibrary')
}
https://i.stack.imgur.com/qFl3Q.png
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')
}
https://i.stack.imgur.com/zXZg3.png
What's the difference between them and what should I be using?
https://i.stack.imgur.com/0thTS.png
If you're using compile
or 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 (option #1)
https://i.stack.imgur.com/e9bzU.png
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
.
Note:
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
compile
does not guarantee the same things that api
guarantees.
Compile
configuration was deprecated and should be replaced by implementation
or api
.
You can read the docs at the API and implementation separation section.
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.
https://i.stack.imgur.com/DDRqz.png
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
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| 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. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
implementation
followed by a runtime
.
Gradle dependency configuration
Gradle 3.0
introduced next changes:
compile -> api api keyword is the same as deprecated compile which expose this dependency for all levels
compile -> implementation Is preferable way because has some advantages. implementation expose dependency only for one level up at build time (the dependency is available at runtime). As a result you have a faster build(no need to recompile consumers which are higher then 1 level up)
provided -> compileOnly This dependency is available only in compile time(the dependency is not available at runtime). This dependency can not be transitive and be .aar. It can be used with compile time annotation processor[About] and allows you to reduce a final output file
compile -> annotationProcessor Very similar to compileOnly but also guarantees that transitive dependency are not visible for consumer
apk -> runtimeOnly Dependency is not available in compile time but available at runtime.
api = public
, implementation = internal
and compileOnly = private
- I need to create such aliases for these functions as they are super confusing.
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.
Since version 5.6.3 Gradle documentation provides simple rules of thumb to identify whether an old compile
dependency (or a new one) should be replaced with an implementation
or an api
dependency:
Prefer the implementation configuration over api when possible This keeps the dependencies off of the consumer’s compilation classpath. In addition, the consumers will immediately fail to compile if any implementation types accidentally leak into the public API. So when should you use the api configuration? An API dependency is one that contains at least one type that is exposed in the library binary interface, often referred to as its ABI (Application Binary Interface). This includes, but is not limited to: types used in super classes or interfaces types used in public method parameters, including generic parameter types (where public is something that is visible to compilers. I.e. , public, protected and package private members in the Java world) types used in public fields public annotation types By contrast, any type that is used in the following list is irrelevant to the ABI, and therefore should be declared as an implementation dependency: types exclusively used in method bodies types exclusively used in private members types exclusively found in internal classes (future versions of Gradle will let you declare which packages belong to the public API)
implementation: mostly we use implementation configuration. It hides the internal dependency of the module to its consumer to avoid accidental use of any transitive dependency, hence faster compilation and less recompilation.
api: must be used very carefully, since it leaks the to consumer’s compile classpath, hence misusing of api could lead to dependency pollution.
compileOnly: when we don’t need any dependency at runtime, since compileOnly dependency won’t become the part of the final build. we will get a smaller build size.
runtimeOnly: when we want to change or swap the behaviour of the library at runtime (in final build).
I have created a post with an in-depth understanding of each one with Working Example: source code
https://medium.com/@gauraw.negi/how-gradle-dependency-configurations-work-underhood-e934906752e5
https://i.stack.imgur.com/6TM4D.png
Some notes before going ahead; compile is deprecated and the docs state you should use implementation because compile will be removed in Gradle version 7.0. If you run your Gradle build with --warning-mode all
you will see the following message;
The compile configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 7.0. Please use the implementation configuration instead.
Just by looking at the image from the help pages, it makes a lot of sense.
So you have the blue boxes compileClasspath
and runtimeClassPath
.
The compileClasspath
is what is required to make a successful build when running gradle build
. The libraries that will be present on the classpath when compiling will be all libraries that are configured in your gradle build using either compileOnly
or implementation
.
Then we have the runtimeClasspath
and those are all packages that you added using either implementation
or runtimeOnly
. All those libraries will be added to the final build file that you deploy on the server.
As you also see in the image, if you want a library to be both used for compilation but you also want it added to the build file, then implementation
should be used.
An example of runtimeOnly
can be a database driver.
An example of compileOnly
can be servlet-api.
An example of implementation
can be spring-core.
https://i.stack.imgur.com/cSbWr.png
When you declare a dependency in a gradle project the codebase + its dependencies (declared as api) can by used by the consumer gradle project.
Lets take an example
We have level 1, level 2, level 3 as gradle projects.
level 1 uses level 2. level 2 uses level 3.
level 1 <- level 2 <- level 3
using api and implementation, we can control if classes of level 3 should be exposed to level 1.
https://i.stack.imgur.com/aXNmY.png
How does this make build faster:
Any changes in level 3. doesn't required re-compilation of level 1. Especially in development, saves time.
Other answers explained the difference.
Just make sure that for Kotlin DSL (build.gradle.kts), the functions should have parentheses and their string arguments enclosed in double quotes instead of single quotes:
Groovy (build.gradle) implementation 'com.android.support:appcompat-v7:25.0.0' testImplementation 'junit:junit:4.12'
Kotlin (build.gradle.kts) implementation("com.android.support:appcompat-v7:25.0.0") testImplementation("junit:junit:4.12")
Success story sharing
implementation
hiding the dependency. Does my question make sense?implementation
only x api will be exposed, but if you useapi
y,z also will be exposed.