My title isn't exactly clear so I'll describe my situation before asking the question:
I have a number of libraries (~15), some of which depend on each other and a number of apps (~5) that depend on various subsets of these libraries. The libraries are hosted on an internal maven site. Most of these apps function within the same domain and there's an overlap in functionalities as well as data models used. Sometimes, adding a feature means making changes to a library in the middle of the hierarchy tree, providing support for it upstream (libs towards the root of the dependency tree), and then utilizing the changes upstream (applications as well as libraries that use the changed libraries).
I want to:
Fetch these libraries from Maven when I'm working on application code
Edit and compile these libraries from within my project while I'm working on changes that affect libraries
My current process is a bit tedious:
Checkout the source for the library(ies) and place it beside my application code (workspace/app_code, workspace/lib1_code, workspace/lib2_code, etc.)
I add include ':lib1_code', include ':lib2_code', etc. to my settings.gradle (I have these commented out so I just toggle them on and off as needed)
I replace the
implementation "com.packages:lib1:1.2.3" with implementation project(':lib1_code') (and so on)
I do the same in the dependent libraries too.
As you can see, it's a lot of work and nobody on my team (myself included) likes the process.
I want to be able to just do all of the above with a set of properties like buildLib1UseLocal=true
Questions:
How common is my scenario?
Does gradle have support for something like this?
Is there another build system that does?
Gradle is based on Groovy, so it provides full scripting support. You can just evaluate the property on your own:
dependencies {
if (buildLib1UseLocal) {
implementation project(':lib1_code')
} else {
implementation 'com.packages:lib1:1.2.3'
}
}
Gradle even provides a feature called dependency substitutions. It allows you to define your dependencies in the regular way, but to resolve them from other sources:
dependencies {
implementation 'com.packages:lib1:1.2.3'
}
configurations.all {
resolutionStrategy.dependencySubstitution {
if (buildLib1UseLocal) {
substitute module('com.packages:lib1:1.2.3') with project(':lib1_code') because '<some reason>'
}
}
}
Related
I am developing my own SDK, which in turn depends on some 3rd party SDKS. For example - OkHttp.
Should I add OkHttp to my build.gradle, or let the users of my SDK include that? In this scenario, they will probably "anyway" use it, so its safe to say they already have it.
Another point to add - not all paths of my SDK needs "OkHttp", so, in theory, some user of my SDK could use those parts only, and have not OkHttp on his APK.
Another thing I am contemplating:
If I do embed OkHttp on by build.gradle - how can users of my SDK use that OkHttp library, instead of consuming another replica?
Should I add OkHttp to my build.gradle, or let the users of my SDK include that?
Adding the dependencies in build.gradle doesn't mean packaging the dependencies inside the aar file.
The aar file doesn't contain the transitive dependencies and doesn't have a pom file which describes the dependencies used by the library.
Uploading the artifact in a maven repository you will have your aar and a pom file which will contains the dependencies list.
In this way gradle will automatically download all the dependencies tree and you can configure gradle to exclude same libraries.
Use implementation and package it - the consumer can still exclude it.
One cannot depend on something and then not package it; this won't build.
In the application package, it can/must only exists once ...so what's the point?
I will answer your questions in a reverse order
Another thing I am contemplating: If I do embed OkHttp on by build.gradle - how can users of my SDK use that OkHttp library, instead of consuming another replica?
How Gradle build system works is suppose, In my project I use your library and I'm using v2 of OkHttp and your library is using V1 of OkHttp, then the gradle will automatically use the latest version. You can read about it here
Another point to add - not all paths of my SDK needs "OkHttp", so, in theory, some user of my SDK could use those parts only, and have not OkHttp on his APK.
In my project I use your library and it uses OkHttp, whereas I don't use it in my project also, I'm not using the part of your library where you are using OkHttp but still my APK will include OKHttp in it. This can be avoided either by splitting your library into two separate libraries or me using proguard in my Project.
Should I add OkHttp to my build.gradle, or let the users of my SDK include that? In this scenario, they will probably "anyway" use it, so its safe to say they already have it.
You should not bundle it in your library you just use implementation and let the user of your library decide if he wants to exclude it or not.
You need knows about api and implementation in the gradle
The link will be helpful
Api:
Role: Declaring ,API, dependencies
Consumable? no
Resolveable? no
Description: This is where you should declare dependencies which are transitively exported to consumers, for compile.
Implemetation:
Role: Declaring, implementation, dependencies
Consumable? no
Resolveable? no
Description: This is where you should declare dependencies which are purely internal and not meant to be exposed to consumers.
It would be easier for me to show you but the long story short.
Main Application
Created a Library lets call it SECOND
Created a Shopping List Library call it THIRD
When I add my THIRD dependency to my SECOND library when using implementation in the gradle file, I am not able to implement interfaces for some reason. When using api it works just fine.
Also, we are adding this by importing the aar and pom file manually.
Project Level Gradle For SECOND
allprojects {
repositories {
google()
jcenter()
maven { url "$projectDir/../THIRD" }
}
}
Only way to actually allow access to the interfaces is to use API
api('com.THIRD.#aar')
This is quite as expected: declarations from implementation dependencies of a library are not visible during compilation of the library usages and are only available at runtime.
On contrary, api dependencies are visible during compilation of the library usages, too.
You should only use the implementation configuration if you don't want the library users to see the declaration from a dependency, which is certainly not the case if you expect the user to implement an interface from the dependency.
See: Gradle dependency configuration: implementation vs api vs runtimeonly vs compileonly
I know the difference between the two as discussed here.
As Android developer,
Why I should care about this?
In gradle, why should I use compileOnly vs implementation/api?
Why I should care about this?
To make your apps build but not ship with unnecessary stuff.
In gradle, why should I use compileOnly vs implementation/api?
The documentation for compileOnly gives one use case as an example:
Gradle adds the dependency to the compilation classpath only (it is not added to the build output). This is useful when you're creating an Android library module and you need the dependency during compilation, but it's optional to have present at runtime. That is, if you use this configuration, then your library module must include a runtime condition to check whether the dependency is available, and then gracefully change its behavior so it can still function if it's not provided. This helps reduce the size of the final APK by not adding transient dependencies that aren't critical. This configuration behaves just like provided (which is now deprecated).
source
For example, consider a push messaging library that supports both Firebase FCM and Amazon ADM but does not require either. It would unnecessarily bloat apps if it would ship with both as transitive dependencies. With compileOnly the library can still be built. Developers using the library can select which dependencies to actually use.
Another example could be compile-time annotations that do not need to ship with the application.
I have seen these terms in android gradle files and its quite confusing i have seen these terminologies while creating instant app. Can someone explain usage of these terms:
api "com.android.support:appcompat-v7:$rootProject.supportLib"
feature project(':main')
application project(':installed')
implementation project(":base")
compile project(":base")
I assume, that all listed terms appear in a dependencies block. Like the name indicates, the project dependencies are defined in this block.
The block provides a method for each registered configuration. These methods are the first part of the listed terms (api, feature ...). As example, you could write the first term as:
api("com.android.support:appcompat-v7:$rootProject.supportLib")
Groovy, where Gradle is based upon, allows omitting unnecessary brackets.
The different configurations must either be defined in the configurations block or by a Gradle plugin. The compile configuration is defined by the Java Plugin, api and implementation by the Java Library Plugin. Personally, I do not know plugins, that define a feature or application configuration. Different configurations can be resolved for different uses, as an example, here is the difference between 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.
The second part of your terms are the dependency definitions, as defined here. The first term defines a dependency on an external module (in a Maven repository), all other terms define a dependency on an other Gradle project in a multi-project build.
There is also a special Groovy feature in your first term. Groovy distinguishes between a regular String (defined via 'my string') and a GString (defined via "my string"). GStrings allow you to use and evaluate Groovy expressions inside a String via "my string with $expression". In this term, rootProject.supportLib is evaluated to define the version of the external module dependency. As an alternative, you could use the map notation instead of the string notation for the dependency:
api group: 'com.android.support', name: 'appcompat-v7', version: rootProject.supportLib
I'm trying to figure out what is the difference between api and implementation configuration while building my dependencies.
In the documentation, it says that implementation has better build time, but, seeing this comment in a similar question I got to wonder if is it true.
Since I'm no expert in Gradle, I hope someone can help. I've read the documentation already but I was wondering about an easy-to-understand explanation.
Gradle compile keyword was deprecated in favor of the api and implementation keywords to configure dependencies.
Using api is the equivalent of using the deprecated compile, so if you replace all compile with api everything will works as always.
To understand the implementation keyword consider the following example.
EXAMPLE
Suppose you have a library called MyLibrary that internally uses another library called InternalLibrary. Something like this:
// 'InternalLibrary' module
public class InternalLibrary {
public static String giveMeAString(){
return "hello";
}
}
// 'MyLibrary' module
public class MyLibrary {
public String myString(){
return InternalLibrary.giveMeAString();
}
}
Suppose the MyLibrary build.gradle uses api configuration in dependencies{} like this:
dependencies {
api(project(":InternalLibrary"))
}
You want to use MyLibrary in your code so in your app's build.gradle you add this dependency:
dependencies {
implementation(project(":MyLibrary"))
}
Using the api configuration (or deprecated compile) you can access InternalLibrary in your application code:
// Access 'MyLibrary' (granted)
MyLibrary myLib = new MyLibrary();
System.out.println(myLib.myString());
// Can ALSO access the internal library too (but you shouldn't)
System.out.println(InternalLibrary.giveMeAString());
In this way the module MyLibrary is potentially "leaking" the internal implementation of something. You shouldn't (be able to) use that because it's not directly imported by you.
The implementation configuration was introduced to prevent this.
So now if you use implementation instead of api in MyLibrary:
dependencies {
implementation(project(":InternalLibrary"))
}
you won't be able to call InternalLibrary.giveMeAString() in your app code anymore.
This sort of boxing strategy allows Android Gradle plugin to know that if you edit something in InternalLibrary, it must only trigger the recompilation of MyLibrary and not the recompilation of your entire app, because you don't have access to InternalLibrary.
When you have a lot of nested dependencies this mechanism can speed up the build a lot. (Watch the video linked at the end for a full understanding of this)
CONCLUSIONS
When you switch to the new Android Gradle plugin 3.X.X, you should replace all your compile with the implementation keyword *(1). Then try to compile and test your app. If everything it's ok leave the code as is, if you have problems you probably have something wrong with your dependencies or you used something that now is private and not more accessible. *Suggestion by Android Gradle plugin engineer Jerome Dochez (1))
If you are a library mantainer you should use api for every dependency which is needed for the public API of your library, while use implementation for test dependencies or dependencies which must not be used by the final users.
Useful article Showcasing the difference between implementation and api
REFERENCES
(This is the same video splitted up for time saving)
Google I/O 2017 - How speed up Gradle builds (FULL VIDEO)
Google I/O 2017 - How speed up Gradle builds (NEW GRADLE PLUGIN 3.0.0 PART ONLY)
Google I/O 2017 - How speed up Gradle builds (reference to 1*)
Android documentation
I like to think about an api dependency as public (seen by other modules) while implementation dependency as private (only seen by this module).
Note, that unlike public/private variables and methods, api/implementation dependencies are not enforced by the runtime. This is merely a build-time optimization, that allows Gradle to know which modules it needs to recompile when one of the dependencies changes its API.
Consider you have app module which uses lib1 as a library and lib1 uses lib2 as a library. Something like this: app -> lib1 -> lib2.
Now when using api lib2 in lib1, then app can see lib2 code when using: api lib1 or implementation lib1 in the app module.
BUT when using implementation lib2 in lib1, then app can not see the lib2 code.
Please refer the link: Android Studio Dependency Configuration available at android developers' official site.
Inside the dependencies block, you can declare a library dependency using one of several different dependency configurations (such as implementation shown above). Each dependency configuration provides Gradle with different instructions about how to use the dependency.
implementation
Gradle adds the dependency to the compile classpath and packages the dependency to the build output. However, when your module configures an implementation dependency, it's letting Gradle know that you do not want the module to leak the dependency to other modules at compile time. That is, the dependency is available to other modules only at runtime.
Using this dependency configuration instead of api or compile (deprecated) can result in significant build time improvements because it reduces the number of modules that the build system needs to recompile. For example, if an implementation dependency changes its API, Gradle recompiles only that dependency and the modules that directly depend on it. Most app and test modules should use this configuration.
api
Gradle adds the dependency to the compile classpath and build output. When a module includes an api dependency, it's letting Gradle know that the module wants to transitively export that dependency to other modules, so that it's available to them at both runtime and compile time.
This configuration behaves just like compile (which is now deprecated), but you should use it with caution and only with dependencies that you need to transitively export to other upstream consumers. That's because, if an api dependency changes its external API, Gradle recompiles all modules that have access to that dependency at compile time. So, having a large number of api dependencies can significantly increase build time. Unless you want to expose a dependency's API to a separate module, library modules should instead use implementation dependencies.
From gradle documentation:
Let’s have a look at a very simple build script for a JVM-based project.
plugins {
id 'java-library'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.hibernate:hibernate-core:3.6.7.Final'
api 'com.google.guava:guava:23.0'
testImplementation 'junit:junit:4.+'
}
implementation
The dependencies required to compile the production source of the project which are not part of the API exposed by the project. For example the project uses Hibernate for its internal persistence layer implementation.
api
The dependencies required to compile the production source of the project which are part of the API exposed by the project. For example the project uses Guava and exposes public interfaces with Guava classes in their method signatures.
Answers from #matpag and #dev-bmax are clear enough to make people understand different usages between implementation and api. I just want to make an extra explaination from another angle, hopes to help for peoples that have the same question.
I created two projects for testing :
project A as a java library project named 'frameworks-web-gradle-plugin' depends on 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE'
project B depends on project A by implementation 'com.example.frameworks.gradle:frameworks-web-gradle-plugin:0.0.1-SNAPSHOT'
The dependencies hierarchy descripted above looks like:
[project-b] -> [project-a] -> [spring-boot-gradle-plugin]
Then I tested following scenarios:
Make project A depends on 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE' by implementation .
Run gradle dependencies command in a terminal in poject B root dir,with following screenshot of output we can see that 'spring-boot-gradle-plugin' appears in runtimeClasspath dependencies tree, but not in compileClasspath's, I think that's exactly why we can't make use of library that declared using implementation, it just won't through compilation.
Make project A depends on 'org.springframework.boot:spring-boot-gradle-plugin:1.5.20.RELEASE' by api
Run gradle dependencies command in a terminal in poject B root dir again.
Now 'spring-boot-gradle-plugin' appears both in compileClasspath and runtimeClasspath dependencies tree.
A significant difference I noticed is that the dependency in producer/library project declared in implementation way won't appear in compileClasspath of consumer projects, so that we can't make use of corresponding lib in the consumer projects.
One more technical note regarding api vs implementation. Suppose you have following dependencies:
dependencies {
api "com.example:foo:1.0"
implementation "com.example:bar:1.0"
}
If you install a generated jar file in your local Maven repository (with help of maven-publish plugin) you will see that generated pom.xml file will look like this:
<dependency>
<groupId>com.example</groupId>
<artifactId>foo</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>bar</artifactId>
<version>1.0</version>
<scope>runtime</scope>
</dependency>
Note: api was converted to compile scope and implementation - to runtime scope.
That allows for consumers of this library to avoid having runtime dependencies in their compile classpath.
Now there is good explanation in the documentation
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.