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.
Related
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>'
}
}
}
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.
Previously my gradle used to look like this and worked fine (apart from few registered bugs)
implementation 'com.dji:dji-sdk:4.3.2'
Now, after changing to
implementation 'com.dji:dji-sdk:4.4.0'
the Camera and other files cannot be recognized anymore. I am attaching a screenshot of the unrecognized imports.
However when I am trying to add
//dji-drones-sdk
implementation 'com.dji:dji-sdk:4.4.0'
provided 'com.dji:dji-sdk-provided:4.4.0'
I am getting "could not download dji-sdk-provided.jar"
Screenshot attached
All the examples and github codes are in version 4.3.2. Can anyone help me out?
Here is the link to the dji sdk
I have found the issue. After Gradle 3.4, the "provided" is replaced by "compileOnly"
I quote,
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).
Hence using compileOnly in place of provided will do the trick.
Here is a link to the gradle changes documentation
Unable to understand the latest gradle dependency configurations which are introduced in Android Studio 3.0 i.e. implementation, api , compileonly and runtimeonly.
Please refer the link : Android Studio 3.0 New Gradle Configuration available at android developers official site.
Based on description mentioned in above link:
implementation: When your module configures an implementation dependency, it's letting Gradle know that the module does not want 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 can result in
significant build time improvements because it reduces the amount of
projects 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: 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), and you should typically use this only in
library modules. 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 times. Unless you want
to expose a dependency's API to a separate test module, app modules
should instead use implementation dependencies.
compileOnly: 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).
runtimeonly: Gradle adds the dependency to the build output only, for use during runtime. That is, it is not added to the compile
classpath. This configuration behaves just like apk (which is now
deprecated).
Suppose, I want to create android library with Retrofit 2.0.0 dependency. What if developer, that will use my library also would have dependency with Retrofit lower version (i.e. 1.9.0) in his project , how gradle will resolve this ? Also, Is it bad approach to use libraries like Retrofit in my own library to simplify code?
Gradle has a number of dependency resolution strategies, you can take a look at the dependency management part of the official Gradle user guide. Especially at 52.2.3. Resolve version conflicts.
The most common strategies are Newest:
Newest: The newest version of the dependency is used. This is Gradle's default strategy, and is often an appropriate choice as long as versions are backwards-compatible.
And Fail:
Fail: A version conflict results in a build failure. This strategy requires all version conflicts to be resolved explicitly in the build script. See ResolutionStrategy for details on how to explicitly choose a particular version.
So, the behaviour of dependency resolution in gradle build script depend on what resolution strategy is used. In most cases, the newer version will be used.
The questions like "Is it bad approach to use libraries like Retrofit in my own library to simplify code?" are mostly opinion based, as for me. You are able to use anything you need, especially if you can't do something without it or it'll take too long to implement it yourself. Tools like a Gradle are made to simplify your life and try to solve most of the problems you can catch with transitive dependencies and you don't have to worry much about it. If you are not creating some widely used library, which should run everywhere no doubt.