Android gradle 4 dependency rules in Android Studio 3

As we are currently moving slightly to AS3 , there are a few changes in project configuration have been made recently with new gradle4,

You have probably noticed starting from Android Studio 3,dependency is declared in Build.gradle this way:

dependencies {

 api “com.google.android.gms:play-services-location:9.4”

 implementation "com.google.android.gms:play-services-location:10.2”

}

The ‘compile' configuration is deprecated in Gradle Tool 4!

Documentation says:

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

If you run commande “gradlew release-build” compiling with gradle 4 , generates 
warnings about ‘deprecated compile’ 


What are api & implementation and where they comes?

From Android gradle 4 , the plugin exposes two configuration for dependency 

  • api:this is by default the old (predicted) “compile” which compile and build Library
  • implementation:this is a new configuration , which compile only when Library changed its public 
    exposed API and require a new complete build.



How it works:

Pair of Implementation/api instead of compile:

So far we have used compile to build internal or external dependency in build.gradle

I think we are familiar enough with this kind of build dependency :

compile "com.android.support:design: xyz

However with new changes made in gradle new version ‘compile’ is deprecated and we are encouraged to use either ‘api’ or ’implementation’ instead like this:

api "com.android.support:design: xyz

or

implementation "com.android.support:design: xyz


This is the rules:


  1. compile' should be changed to => ’ implementation' or ‘api’ according to concept we describe below

Example:

compile "com.android.support:design:xyz"

Should be changed to:

implementation "com.android.support:design: xyz"

or

api "com.android.support:design: xyz"


The same way  in a Custom internal Library

dependencies {

    compile project(':MyLibrary’)
}

Should be changed to:

dependencies {

    implementation project(':MyLibrary’)
}



  1. debugCompile should be changed to => debugImplementation

Example:

debugCompile "com.squareup.leakcanary:leakcanary-android: xyz

Should be changed to:

debugImplementation "com.squareup.leakcanary:leakcanary-android: xyz




  1. releaseCompile should be changed to => releaseImplementation

Example:

releaseCompile "com.squareup.leakcanary:leakcanary-android-no-op: xyz

Should be changed to:

releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op: xyz



For unit test the same rule apply , so for example the following old fashion line :

  1. testCompile should be changed to => testImplementation 

Example:

testCompile "org.mockito:mockito-core: xyz

Should be changed to:

testImplementation "org.mockito:mockito-core: xyz



  1. androidTestCompile should be changed to => androidTestImplementation

Example: 

androidTestCompile "junit:junit:xxx"

Should be changed to:

androidTestImplementation "junit:junit:xxx"


What is different between ‘api' and ‘implementation

Take a look at this dependency graph , every Library expose some public API to others ,we refer to those api as a leak api , but there are many internal implementation that maybe private and not interacting directly to out side, Compiler resolve dependency by building all libraries in order to keep safe integrity of application , if a internal function changed Library1 without any change in exposed API , all other libraries using Library1 get compiled and built again, that results in more compile time, suppose there are a few changes made in a custom module library or a gradle library that is purely internal and doesn’t impact exposed api prototype , so why we should compile all libraries again which just increase compile time? ‘compile’ will build all library belonging to dependency regardless of changes in exposed or internal functions and gradle4 comes to rescue us with two configuration , api and implementation,

 ‘api' is working like old compile , so compiling all libraries (library1 , library2,Mockito,….)  from schratch ,
But 'implementation’ don’t care of this changes when it’s only internal and not impacting API, in this case so called Leak API reamin as before for the world outside.so only the library in question (changed library) will be build and that increase 
significantly the compile time and performance.


Another advantage of this concept is to let developers keep debugging on multiple version of a libraries , for example to stay on old version if the new one is facing an issue  by playing with library different versions, this why they can track and find issues more smoothly , the following dependencies shows two different versions in use .

dependencies {

 api “com.google.android.gms:play-services-location:9.4”

 implementation "com.google.android.gms:play-services-location:10.2”

}

[Be careful u cannot use both api and implementation at the same time !]


Khosrov August 08, 2017


 © Xosrov 2016