Android App Development

Android Build Types and Product Flavors

January 16, 2023

In this article, I will discuss the details and differences of using Build Types and Product Flavors in Android. Both structures provide many conveniences when creating Android applications. We can benefit from many points, from the icons of the applications to the production versions, and from special configurations for development mode to creating different variants of the application.

Android Build Types

Build Types help us customize certain features used by Gradle when developing, building, and preparing signed versions of applications. They also make it easier for us to manage certain configurations that we will use. By default, two Build Types are defined in newly created Android projects: one is the debug mode, and the other is the release mode.

We can add a new type. Based on the types, we can add different version extensions or completely custom variables. During development, you can easily switch between these different types using the Build Variants tab located at the bottom left of Android Studio as a shortcut.

In the code example below, the MODE variable will be updated based on the selected type in the automatically created BuildConfig.java class after the code is compiled for the first time. You can easily access these static variables within code blocks. You can also apply your environment changes here. Thanks to this flexibility, you can set up a more secure and flexible development environment using special configurations for the development mode.

buildTypes {
debug {
signingConfig signingConfigs.debug
shrinkResources false
minifyEnabled = false
debuggable true
resValue "string", "google_maps_key", (project.findProperty("GOOGLE_MAPS_API_KEY_DEBUG") ?: "")
buildConfigField "MODE", "DEBUG"
ext.betaDistributionReleaseNotesFilePath = "release_notes.txt"
ext.betaDistributionEmailsFilePath = "testers.txt"
ndk {
abiFilters "x86", "armeabi-v7a", "arm64-v8a"
}
}

release {
signingConfig signingConfigs.release
shrinkResources true
minifyEnabled true
debuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
resValue "string", "google_maps_key", (project.findProperty("GOOGLE_MAPS_API_KEY") ?: "")
buildConfigField "MODE", "PROD"
ext.betaDistributionReleaseNotesFilePath = "release_notes.txt"
ext.betaDistributionEmailsFilePath = "testers.txt"

ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}

pilot {
debuggable = true
signingConfig signingConfigs.debug
debuggable true
resValue "string", "google_maps_key", (project.findProperty("GOOGLE_MAPS_API_KEY") ?: "")
ext.betaDistributionReleaseNotesFilePath = "release_notes.txt"
ext.betaDistributionEmailsFilePath = "testers.txt"
buildConfigField "MODE", "PILOT"

ndk {
abiFilters "x86", "armeabi-v7a", "arm64-v8a"
}
}
}

Product Flavors

Product Flavors is a structure similar to Build Types that provides flexibility. The main difference is that it allows us to generate different versions of the application from a shared codebase.

Android Product Flavors are used to create different versions of an app. These versions can have different features, such as being free or paid, having different themes and texts, or using different environments or APIs. For example, you want to create both a paid and a free version of the same app, with only minor differences between the two. Instead of using a separate codebase for each app, you can use the Product Flavors structure to create multiple versions using a shared codebase. With Product Flavors, you can easily manage many features of the app, from the name to the icon, version information to the API environment to be used.

flavorDimensions "env", "api"
productFlavors {
prod {
dimension "env"
resValue "string", "app_name", '"Production"'
manifestPlaceholders = [
appIcon: "@mipmap/ic_launcher",
appIconRound: "@mipmap/ic_launcher_round"
]

applicationIdSuffix ".release"

buildConfigField "String", "FLAVOR_TYPE", '"PRODUCTION"'
buildConfigField "String", "DATABASE", '"PRODUCTION_DB"'
}

test {
dimension "env"
resValue "string", "app_name", '"Test"'
manifestPlaceholders = [
appIcon: "@mipmap/ic_launcher_test",
appIconRound: "@mipmap/ic_launcher_test_round"
]

applicationIdSuffix ".test"

buildConfigField "String", "FLAVOR_TYPE", '"test"'
buildConfigField "String", "DATABASE", '"TEST_DB"'
}

minApi28 {
dimension "api"
minSdkVersion 23
versionCode 10000 + defaultConfig.versionCode
versionNameSuffix "-minApi28"
}

minApi24 {
dimension "api"
minSdkVersion 21
versionCode 10000 + defaultConfig.versionCode
versionNameSuffix "-minApi24"
}
}

I have a few points that I’d like to explain about the code block I shared above. Firstly, I have defined two dimensions with Flavor Dimensions: one is “env” and the other is “api”. With this feature, we can group based on these dimensions and use their combinations. That is, when we first compile the build.gradle by adding the code block above, the result of the combination between the “env” and “api” groups will appear in the Build Variants tab. You can see the output of this in the image below. For example, we get a new variant version by combining the test variant with the Minimum Api 24 variant. This way, we can make separate arrangements for APIs 24 and 28 with the same codebase, and distribute and manage them separately. We can make the names of the applications different from each other with “res_value” parameter. With “manifest placeholders” parameter, we can also use the application icons in different ways. Changing the app name with manifest placeholders is also possible.

Product Flavors structure also allows you to define custom variables in the same way as the Build Types structure. Similarly, you can access these static variables through the BuildConfig.java class

Author: Erdi Koç

Tags

Android Build Types Product