Several gradle chips for your Android application

One of the latest Android Weekly newsletters included an article that mentioned interesting features of the project’s assembly organization. After reading it, I wanted to share some of what I use to configure the assembly of the Android project.
Getting rid of code duplication in your build.gradle files
It would seem a simple idea, but this approach is used quite rarely.
Suppose you have several modules in the application, in each of which you need to register buildToolsVersion. Often this problem is solved by putting a specific version into an ext-variable.
In addition, you can optimize the code by setting this value in only one place.
Let me remind you of the possibility of using code from another gradle file in your build.gradle:
apply plugin: 'com.android.application'
apply from: "$buildSystemDir/application.gradle"
And in the application.gradle file, you can already specify the necessary versions:
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
minSdkVersion 15
targetSdkVersion 24
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
Here you can make any settings for the entire project. Then, in each specific module, you can override the values (if necessary).
In a separate gradle file, you can take out not only the android - extension configurations, but also test dependencies, jacoco, findbugs, pmd settings, etc. etc.
Add properties to the project
You may have already noticed the $ buildSystemDir variable. It is specified in the build.gradle file of the root project:
allprojects {
it.extensions.add("buildSystemDir", "$rootProject.projectDir/buildSystem/")
}
After that, in each module, you can use this property without any prefixes. Which, again, slightly reduces the code;)
Signature apk
The topic of securely storing keys and passwords for a certificate is very interesting, and I will be very happy if someone tells me a good solution (with ci, safe), but for now I’ll give you my version.
Information about each certificate is stored in a * .properties file. It may or may not be present on the project assembly machine.
sign.gradle:
Properties signProp = new Properties()
try {
signProp.load(rootProject.file('signForTest.properties').newDataInputStream())
project.ext {
forTest = new Object() {
def storeFile = signProp.get("forTest.storeFile")
def storePassword = signProp.get("forTest.storePassword")
def keyAlias = signProp.get("forTest.keyAlias")
def keyPassword = signProp.get("forTest.keyPassword")
}
}
} catch (IOException e) {
project.ext {
forTest = new Object() {
def storeFile = "/"
def storePassword = ""
def keyAlias = ""
def keyPassword = ""
}
}
}
android {
signingConfigs {
forTest {
storeFile file(project.ext.forTest.storeFile)
storePassword project.ext.forTest.storePassword
keyAlias project.ext.forTest.keyAlias
keyPassword project.ext.forTest.keyPassword
}
}
}
That's all. It remains to apply this file in the build.gradle of our application and use the configured signingConfig in our buildType.
buildSrc
What to do when the build system needs complete code, and there is no time to write your plugin? To do this, you can use the buildSrc directory.
In the root directory of your project (where gradlew lies), create a new buildSrc module. This module should be a regular java project (or groovy or something else), with the following build.gradle file:
apply plugin: "groovy"
repositories {
mavenCentral()
}
dependencies {
compile localGroovy()
compile gradleApi()
}
// START SNIPPET addToRootProject
rootProject.dependencies {
runtime project(path)
}
// END SNIPPET addToRootProject
Now in this project you can create some class Awesome.groovy, and import and use this class in the build.gradle Android module.
When you start building your project, the buildSrc module will be executed first. After that, the remaining modules will be configured.
I can’t say that I recommend using this solution, because this slightly increases the build time of the project from scratch. But in extreme cases it can come in handy.
Flavor dimensions
This feature for configuring a project is well described in the docks (// tools.android.com/tech-docs/new-build-system/user-guide#TOC-Multi-flavor-variants
). But either not everyone knows about it, or they don’t understand in what cases it can be used, so I decided to talk about it.
Suppose you need to create a paid and free application. At the same time, your application is released for TVs, tablets and phones. Moreover, your application is published in different markets.
Add the following code to your build.gradle:
android {
flavorDimensions "device", "paid", "market"
productFlavors {
tv {
dimension 'device'
}
tablet {
dimension 'device'
}
phone {
dimension 'device'
}
free {
dimension 'paid'
}
premium {
dimension 'paid'
}
google {
dimension 'market'
}
amazon {
dimension 'market'
}
}
}
By announcing 7 flavors in three different groups, you get as many as 12 different application options. We add here buildTypes, and we get a huge amount of apk-shek.
phoneFreeGoogleDebug
phoneFreeGoogleRelease
phoneFreeAmazonDebug
phoneFreeAmazonDebug
phonePremiumGoogleDebug
.... etc.
Each flavor you declare creates its own sourceSet. For example, if you selected the phoneFreeAmazonDebug flavor, the following sourceSets will be used:
src / phoneFreeAmazon
src / phoneFree
src / phoneAmazon
src / freeAmazon
src / phone
src / free
src / amazon
Thus, there are great opportunities for customizing assemblies.
Specify minSdkVersion for buildType
To speed up the assembly of the application at the development stage, it is often recommended to create a separate flavor “develop” and specify minSdkVersion = 21 for it. However, this is not very convenient, and you often want to specify this parameter in buildType debug. Initially, the build plugin does not allow this, however, if necessary, this problem can be solved by the following hike.
For the desired buildType, you need to add the ext variable:
…
buildTypes {
...
debug {
...
ext.minSdkVersion = 21
}
}
Below you need to add the following code:
preBuild.doFirst {
android.applicationVariants.all {
if (it.buildType.hasProperty("minSdkVersion")) {
int i = it.buildType.ext.minSdkVersion;
it.mergedFlavor.setMinSdkVersion(new com.android.builder.core.DefaultApiVersion(i))
}
}
}
Now for all your flavors in the debug assembly, minSdkVersion will be 21. However, there is a tight tie to the plugin's inside, so something might break when updating the plugin version. Therefore, I can not recommend using this hack - the choice is yours.
Instead of a conclusion, I want to note that Gradle is a very powerful tool for building a project. If you pay a lot of attention to the quality of the code of your application, then do not forget to tidy up the code in the build.gradle files as well.