Android Gradle configurations
The basics of Gradle configurations
Almost all Gradle projects (including all Gradle-based Android projects) uses some configurations. What does configuration mean in Gradle? First take a look at the example:
1 2 3 4 5 6 7 8 9 10 | dependencies { annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' compile 'com.jakewharton:butterknife:8.4.0' compile project(':api') debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4' androidTestCompile 'com.android.support.test:runner:0.5' testCompile 'org.robolectric:robolectric:3.1.2' testAnnotationProcessor 'org.robolectric:robolectric-processor:3.1.2' } |
Configurations are used here to group dependencies, they are defined using pattern configurationName dependencyNotation
. In the example above compile
, testCompile
and so on are all configuration names and 'com.jakewharton:butterknife:8.4.0'
, project(':api')
are dependency notations. All configurations here are created by Android Gradle plugin.
After project evaluation, each dependency is resolved (with the help of repositories
stanza and other rules) to file URI and configuration becomes FileCollection. The latter can be used by various Gradle tasks to achieve their objectives. For example elements of debugCompile configuration are added to debug variant compile classpath.
Configurations in Android projects
Configuration names added by Android Gradle plugin consist of two main parts:
- optional prefix denoting build variant, product flavor or build type
- required suffix denoting scope
Eg. in debugCompile
: debug
is a build type (equal to build variant when there are no product flavors) and compile
. If there is no prefix configuration is common to all build variants, eg. compile
is applicable to both debug
and release
build types. Each regular configuration has also corresponding unit test one, so by default for compile
there is also testCompile
, testDebugCompile
and testReleaseCompile
. Unit test configuration inherits from regular ones (inheritance is described later in this article).
Finally, there is a androidTest
configuration group used for instrumentation tests (running on device/emulator). From configurations point of view androidTest
is treated as a build type. Note that currently (up to Gradle plugin 2.2.0) only one build type can be instrumentation-tested, so there are no corresponding debug and release configurations. There are also wearApp
configurations related to Android Wear, they are not covered by this article.
Scopes
The scope is related to phase when the configuration is used. What phase means here? Let’s take a look a the Figure 1.
annotationProcessor/kapt/apt
This scope is dedicated to annotation processors like dagger-compiler
from Dagger 2. Using this configuration is not strictly required for annotation processing itself to work (provided
or even compile
in some cases also do the job). However additional scope guarantees that transitive dependencies (like guava or javapoet commonly used by annotation processors) won’t be available for use from application code. If provided
is used instead they are available and used by a mistake will cause crash at runtime due to missing classes. In case of compile
all the classes from transitive dependencies will be available but method number (which is limited to 65K for dex file) will often increase drastically.
AnnotationProcessor has been introduced in Android Gradle Plugin 2.2.0. Formerly this functionality was provided by android-apt Gradle plugin. In kotlin this scope is called kapt
.
provided/compileOnly
In maven and Android Gradle Plugin it is called provided
however name compileOnly
is self-explanatory this is how kotlin and Java Gradle plugins call this scope. As the second name suggests dependencies declared in this configuration are available only at the compile time but not at runtime. They are not packaged into APK or AAR so attempt to access classes from those dependencies will cause runtime error. There are 2 important limitations of this scope:
- dependencies are not transitive so they are not included in dependent projects
- dependencies can only be JARs, not AARs meaning they cannot include Android resources, assets, manifests etc.
When should it be used? Typically dependencies containing source-only annotations go here. Some widely used examples of Android applications projects are: Android Support Annotations (however large number of samples use compile
scope for it) or lombok (needs to be also in annotationProcessor
).
compile
This is the most common scope. Dependencies will be available at both compile and execution time, which is desired in most cases. Eg. butterknife is needed during compilation and also in runtime.
apk
This is rarely used the scope, even not mentioned in the official documentation. Dependencies are available at runtime but not during compilation, this may be useful eg. when using @SneakyThrows
annotation from lombok.
Inheritance
Configuration can extend another one which means that it contains also items from parents. Eg. testCompile
extends compile
so all production dependencies can also be used in unit tests but not vice versa. Robolectric classes can be used in unit test sources but not in production code. Analogously debugCompile
extends debug
and so on. Particular configuration can inherit from more than one parent. Note that inheritance must be explicitly declared and naming scheme does not imply it eg. testAnnotationProcessor
does not extend annotationProcessor
.
Custom configurations
Existing configurations can also be extended manually which can be used to achieve useful results. Let’s say that junit and assertj-android are needed in both unit (src/test
) and instrumentation (src/androidTest
) tests. The most straightforward way to achieve that is to declare each dependency separately in appropriate configuration:
1 2 3 4 | testCompile 'com.squareup.assertj:assertj-android:1.1.1' testCompile 'junit:junit:4.12' androidTestCompile 'com.squareup.assertj:assertj-android:1.1.1' androidTestCompile 'junit:junit:4.12' |
As you can see there are repetitions. To avoid them we can introduce additional configuration, let’s call it commonTestCompile
. First we need to create it inside configurations
stanza. Along with creation we can configure inheritance telling that appropriate configurations from Android plugin extends from newly created one:
1 2 3 | configurations { [androidTestCompile, testCompile].each { it.extendsFrom commonTestCompile } } |
Now all dependencies added to commonTestCompile
will be also available in androidTestCompile
and testCompile
so each common dependency can be declared only once:
1 2 | commonTestCompile 'com.squareup.assertj:assertj-android:1.1.1' commonTestCompile 'junit:junit:4.12' |
About the author
Ready to take your business to the next level with a digital product?
We'll be with you every step of the way, from idea to launch and beyond!
how to solve the dependency with gradle for aar package?
Which is while I using aar file dependency ,it can resolve the dependencies of aar file automatic , and I don’t need to config gradle for the dependencies manual.
AAR file itself doesn’t contain any info about dependencies. In maven repos they are located in POM files. Here is an example: https://repo1.maven.org/maven2/io/reactivex/rxjava2/rxandroid/2.0.0-RC1/rxandroid-2.0.0-RC1.pom
So one of the solutions is to create local maven repo, you can specify custom file URL for it.
AAR file itself doesn’t contain any info about dependencies. In maven repos they are located in POM files. Here is an example: https://repo1.maven.org/maven2/io/reactivex/rxjava2/rxandroid/2.0.0-RC1/rxandroid-2.0.0-RC1.pom
So one of the solutions is to create local maven repo, you can specify custom file URL for it.