In Part 1 of this tutorial, you learned how to read and build Gradle files and how to manage dependencies in multiple ways. In this part, youâll learn about slightly more complex parts of Gradle. By the end, youâll be able to:
- Sign your releases and have different build types.
- Create Gradle tasks and plugins.
- Create build flavors for profit.
Getting Started
Download the starter project by clicking the Download Materials link at the top or bottom of the tutorial. Youâll pick up where you left off in Part 1.
This part of the tutorial will focus on how to use Kotlin script, since itâs now the preferred way of writing Gradle files. However, every bit of Kotlin script code will have its Groovy equivalent so you can also learn about it. If the alternative Groovy version does not exist, you can assume that the specific bit of code youâre looking at is identical for both cases.
Getting Ready to Publish: Working with Product Flavors and Build Types
In the last article, you finished building your app. Now, youâre thinking of ways to profit from it :]
One solution is to have multiple versions of your app: a free version and a paid version. Fortunately, Gradle supports this at the build level and allows you to define the boundaries of different build types. But before you get started, you need to understand how Gradle allows you to work with different app versions.
Introducing Build Types
By default, there are two build types â debug and release. The only difference between them is the value of the debuggable
parameter. In other words, you can use the debug version to review logs and to debug the app, but the release type is used to publish your app to the Google Play Store. Configure properties to the build types by adding the following code in the android
block of your module-level build.gradle.kts file:
buildTypes {
release {
}
debug {
}
}
Specify the type-specific settings of your application in the debug
and release
blocks.
Learning About Build Signing
One of the most important configurations of the build is its signature. Without a signature, you wonât be able to publish your application because itâs necessary to verify you as an owner of the specific application. While you donât need to sign the debug build â Android Studio does it automatically â the release build should be signed by a developer.
When your keystore is ready, add the code below in the android
block and above the buildTypes
block (the order of declaration matters) of the module-level build.gradle.kts file:
signingConfigs {
create("release") {
storeFile = file("path to your keystore file")
storePassword = "your store password"
keyAlias = "your key alias"
keyPassword = "your key password"
}
}
If youâre using Groovy, add this code instead:
signingConfigs {
release {
storeFile file("path to your keystore file")
storePassword "your store password"
keyAlias "your key alias"
keyPassword "your key password"
}
}
In the signingConfigs
block, specify your signature info for the build types. Pay attention to the keystore file path. Specify it with respect to the module directory. In other words, if you created a keystore file in the module directory and named it âkeystore.jksâ, the value you should specify will be equal to the name of the file.
Update the buildTypes
block to sign your release build automatically:
release {
signingConfig = signingConfigs.getByName("release")
}
And the Groovy version:
release {
signingConfig signingConfigs.release
}
Or, if youâre using Groovy:
Then, be sure to keep keystorePassword.gradle.kts
ignored by your version control system. Other techniques include keeping the password in an OS-level environment variable, especially on your remote Continuous Integration system, such as CircleCI.
- Once youâve published your app to the Google Play Store, subsequent submissions must use the same keystore file and password, so keep them safe.
- Be sure NOT to commit your keystore passwords to a version control system such as GitHub. You can do so by keeping the password in a separate file from
build.gradle.kts
, saykeystorePassword.gradle.kts
in aSigning
directory, and then referencing the file from the app module-levelbuild.gradle.kts
via:apply(from = "../Signing/keystorePassword.gradle.kts")
apply from: "../Signing/keystorePassword.gradle"
Note: There are two important considerations related to your keystore file:
apply(from = "../Signing/keystorePassword.gradle.kts")
apply from: "../Signing/keystorePassword.gradle"
Using Build Flavors
In order to create multiple versions of your app, you need to use product flavors. Flavors are a way to differentiate the properties of an app, whether itâs free/paid, staging/production, etc.
Youâll distinguish your app flavors with different app names. First, add the following names as strings in the strings.xml file:
<string name="app_name_free">Socializify Free</string>
<string name="app_name_paid">Socializify Paid</string>
And remove the existing:
<string name="app_name">Socializify</string>
Now that the original app_name
string is no longer available, edit your AndroidManifest.xml file and replace android:label="@string/app_name"
with android:label="${appName}"
inside the application
tag.
Next, add the following code in the android
block of your module-level build.gradle.kts file:
// 1
flavorDimensions.add("appMode")
// 2
productFlavors {
// 3
create("free") {
// 4
dimension = "appMode"
// 5
applicationIdSuffix = ".free"
// 6
manifestPlaceholders["appName"] = "@string/app_name_free"
}
create("paid") {
dimension = "appMode"
applicationIdSuffix = ".paid"
manifestPlaceholders["appName"] = "@string/app_name_paid"
}
}
Hereâs whatâs happening in the code above:
- You need to specify the flavor dimensions to properly match the build types. In this case, you need only one dimension â the app mode.
- In the
productFlavors
, specify a list of flavors and their settings. In this case,free
andpaid
. - Specify the name of the first product flavor â
free
. - Itâs mandatory to specify the
dimension
parameter value. Thefree
flavor belongs to theappMode
dimension. - Since you want to create separate apps for free and paid functionality, you need them to have different app identifiers. The
applicationIdSuffix
parameter defines a string thatâll be appended to theapplicationId
, giving your app unique identifiers. - The
manifestPlaceholders
allows you to modify properties in your AndroidManifest.xml file at build time. In this case, modify the application name depending on its version.
The Groovy equivalent would be:
// 1
flavorDimensions = ["appMode"]
// 2
productFlavors {
// 3
free {
// 4
dimension "appMode"
// 5
applicationIdSuffix ".free"
// 6
manifestPlaceholders.appName = "@string/app_name_free"
}
paid {
dimension "appMode"
applicationIdSuffix ".paid"
manifestPlaceholders.appName = "@string/app_name_paid"
}
}
Sync your project with Gradle again. After the project sync, run the tasks
command, and see if you can spot whatâs changed:
./gradlew tasks
Youâll get a similar list of tasks to the one you got when you ran this command the first time:
... Build tasks ----------- ... assembleDebug - Assembles main outputs for all Debug variants. assembleFree - Assembles main outputs for all Free variants. assemblePaid - Assembles main outputs for all Paid variants. assembleRelease - Assembles main outputs for all Release variants. ...
Spot the difference? Look at the tasks under the Build tasks
section, and youâll see some new ones there. You now have separate commands for each build type and build flavor.
Run the command:
./gradlew assembleDebug
When the command completes, check the output directory:
ls -R app/build/outputs/apk
Hereâs what youâll see:
free paid
app/build/outputs/apk/free:
debug
app/build/outputs/apk/free/debug:
app-free-debug.apk output-metadata.json
app/build/outputs/apk/paid:
debug
app/build/outputs/apk/paid/debug:
app-paid-debug.apk output-metadata.json
You should have two builds generated â freeDebug
and paidDebug
.