Build Variants, Product Flavors, and Build Types Concepts in Android App Development
Hello, as I was researching topics I felt lacking in knowledge about, I wanted to share what I’ve learned with you. I will talk about the concepts of build variants, product flavors and build types, which we frequently encounter in Android application development. Let’s start :)
Build Types
Build Types refers to the compilation and packaging settings, signing configurations, and other compilation options of an Android project. Build types are used to compile and package a project for different purposes. Each build type allows you to determine how the compilation process of your project will be managed and which features will be enabled. Here are some typical build types that are commonly found in a project, along with the settings that can be configured for these types:
Debug Build Type:
- Enabling Debugging Mode: The Debug build type enables debugging mode, allowing you to step through your code.
- Increasing Logging Levels: You can increase logging during debugging.
- Signing: Typically signed with a debugging key.
- Disabling Proguard (Code Obfuscation): Proguard, a code obfuscation tool, is usually disabled during debugging.
Release Build Type:
- Code Obfuscation: The Release build type enables code obfuscation tools (e.g., Proguard or R8) to protect your code.
- Resource Shrinking: Removes unnecessary resources or code.
- Signing: Typically signed with the final release key.
- Reducing Logging Levels: Disables unnecessary log messages for debugging.
Staging Build Type:
- Used for development and testing environments.
- Can include custom configurations or targets.
- Signing: Signed with a custom key for development and testing purposes.
Custom Build Types:
- Custom build types can be created for specific project purposes.
- Custom configurations, signatures, and other settings can be added.
Build types accommodate different scenarios for developing and distributing your project. Each build type can be customized based on the needs and distribution requirements of your project. This allows for the use of different settings when debugging during development and when distributing the final version to users. In Android projects, build types have a significant impact on the security, performance, and functionality of the application.
You can create and configure build types as in the example within the Android block of the module-level gradle file.
android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
isDebuggable = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
debug {
isMinifyEnabled = false
isShrinkResources = false
isDebuggable = true
}
}
}
Build types are not restricted to just “debug” and “release” categories. You can create and configure numerous build types according to your requirements.
Product Flavors
Product flavors are a Gradle feature in Android development that allows you to create different variations of your app, making your development process more manageable. This feature enables you to customize your app for different purposes while using the same core codebase. Here are the details of product flavors:
Why Use Them?
- For Different Versions: Product flavors allow you to customize your app for different versions. For example, you can create free and premium versions or different variations like different languages or themes.
- Single Codebase: Product flavors enable you to create different variations while using the same codebase efficiently, making it easier to manage and maintain your code.
- Simplify Distribution: Instead of creating separate projects for different versions, you can simplify distribution using product flavors. This makes updates and maintenance more straightforward.
What Can You Customize?
- Resource Files: You can define custom resource files and layouts for each product flavor. For example, you can include text in different languages or different layouts.
- Dependencies: You can specify different dependencies for different product flavors. For instance, you can use one library in one version and a different one in another.
- Manifest File: Customize the AndroidManifest.xml file for each product flavor. This can be useful for adding different permissions, features, or activities for different versions.
- Build Configurations: You can define custom build configurations for different product flavors. For example, you can use different server URLs or keys.
How to Use Them?
Product flavors are defined in your project’s build.gradle
file. For example, if you want to create "free" and "paid" versions, you can use the following structure:
android {
productFlavors {
create("free") {
applicationIdSuffix = ".free"
}
create("paid") {
applicationIdSuffix = ".paid"
}
}
}
Relationship with Build Variants
Product flavors combine with Gradle’s Build Variants feature. Each product flavor creates one or more build variants. For example, you can create separate variants for “free” and “paid” versions.
Product flavors make Android apps more flexible and customizable, allowing you to create versions tailored to different user groups or needs. This enables you to reach a broader user base and streamline your development process effectively.
Build Variants
The term “Build Variant” refers to creating different versions or variations of an application using the same underlying source code. These versions are customized to meet different requirements, targets, or usage scenarios. Here’s the meaning of this concept and the process after creating a version:
Creating Different Versions: Build Variants allow you to create application versions with different configurations and requirements using the same base source code. These versions represent various variations of the same application.
Example Scenario: For instance, if you are developing an application and want to release both free and paid versions, you can create two different Build Variants using the same underlying codebase. This is used to manage both free and paid versions.
Gradle Process: Build Variants are processed by Gradle. Each Build Variant represents a combination of the relevant Build Type (e.g., “debug” or “release”) and Product Flavor (product variant). Gradle compiles these versions using the specified combinations of resources and configurations.
Outcome: After Build Variants are created, these versions are exported as application APKs or AAB (Android App Bundle) files. Each Build Variant represents a different APK or AAB file. These files can then be used for distribution.
Build Variants enable you to create different application versions and manage each version using the same base source code. This allows you to customize your application to different targets, requirements, or usage scenarios. These versions are then compiled and used for distribution.
Summary
While Product Flavors are used to define different configurations and variations of an application, Build Variants are used to manage distribution. Product Flavors allow you to create different versions of an application, but for distributing these different versions, you use Build Variants. Build Variants represent different combinations of Build Types (e.g., “debug” or “release”) and Product Flavors, and these Build Types produce the distributable versions of the application.
For example, using Product Flavors, you can create customized versions of the application for different clients. However, to distribute these different versions, you would use the Build Variants created for each Product Flavor. Build Variants represent different Build Types for each Product Flavor (for instance, “debug” or “release”), and these Build Types generate the distributable versions of the application.
In summary, while Product Flavors are used to define different configurations of the application, Build Variants compile and distribute these configurations. Build Variants manage the process of creating and distributing different versions (APK or AAB files).
Now let’s implement an example scenario to apply the concepts we have learned on Android. This scenario will use “Product Flavors” to manage free and paid versions of an app, as well as different language versions. Additionally, each version will have a different app name and some language customizations.
First configure the “productFlavors” section in the “app” module of the “build.gradle” file as follows:
android {
...
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
isDebuggable = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
debug {
isMinifyEnabled = false
isShrinkResources = false
isDebuggable = true
}
}
flavorDimensions += listOf("pricing", "language")
productFlavors {
create("free") {
applicationIdSuffix = ".free"
dimension = "pricing"
// resValue("string","app_name","ProductFlavors-Free")
}
create("paid") {
applicationIdSuffix = ".paid"
dimension = "pricing"
// resValue("string", "app_name", "ProductFlavors-Paid")
}
create("english") {
dimension = "language"
}
create("turkish") {
dimension = "language"
}
}
}
This configuration allows you to create different versions of your app for both free and paid versions, as well as different language versions, all using Product Flavors.
In this configuration, a total of four different product flavors have been defined within two dimensions: “pricing” and “language.” Additionally, two different build types, “release” and “debug,” have been specified within the “buildTypes” section. Now, let’s examine the combinations of these components:
- freeEnglishRelease: Free version, English language, and “release” build type. This combination represents an optimized and minimized version of the free version of the application.
- freeEnglishDebug: Free version, English language, and “debug” build type. This combination represents a debug-friendly version of the free version of the application.
- freeTurkishRelease: Free version, Turkish language, and “release” build type. This combination represents an optimized and minimized version of the free version of the application in Turkish.
- freeTurkishDebug: Free version, Turkish language, and “debug” build type. This combination represents a debug-friendly version of the free version of the application in Turkish.
- paidEnglishRelease: Paid version, English language, and “release” build type. This combination represents an optimized and minimized version of the paid version of the application.
- paidEnglishDebug: Paid version, English language, and “debug” build type. This combination represents a debug-friendly version of the paid version of the application.
- paidTurkishRelease: Paid version, Turkish language, and “release” build type. This combination represents an optimized and minimized version of the paid version of the application in Turkish.
- paidTurkishDebug: Paid version, Turkish language, and “debug” build type. This combination represents a debug-friendly version of the paid version of the application in Turkish.
These combinations represent different versions and language options of your application. Each can cater to different usage scenarios or the needs of different target audiences. By using these combinations, you can effectively manage various versions and languages of your application.
You can view the generated combinations in Android Studio by navigating to the “Build” menu and selecting “Select Build Variants.”
Later, organize your project’s folder structure as follows:
While in the Project view, right-click on the ‘src’ folder under the ‘app’ folder, then select New -> Directory. Next, a window like the one below will appear.
You should create separate resource files in each of the generated flavors. Create to the ‘values’ folder inside the ‘res’ folder. In this folder, you will create a new file named ‘strings.xml.’ This file contains the text values for your application. You can follow the steps below to update different application names or other text values for each product flavor:
<resources>
<string name="app_name">Free English App</string>
<string name="greeting">Hello!</string>
</resources>
<resources>
<string name="app_name">Paid Turkish App</string>
<string name="greeting">Merhaba!</string>
</resources>
<resources>
<string name="app_name">Paid English App</string>
<string name="greeting">Hello!</string>
</resources>
<resources>
<string name="app_name">Free Turkish App</string>
<string name="greeting">Merhaba!</string>
</resources>
This way, different application names will be defined for each product flavor.
As a final step, open the layout file of your application’s main Activity (activity_main.xml), and add the appropriate places to display different application names and greeting messages. In this example, let’s assume that we will use two TextViews to display the application name and greeting message.
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@string/app_name"
android:textAlignment="center"
android:textSize="21sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/greeting"
android:textAlignment="center"
android:textSize="21sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
You can then run your app in different versions or configurations by selecting different Build Variants. Screenshots of the selected build variants:
By following these steps, you can customize your Android application for different versions or the requirements of your target audience. This provides you with the ability to make your application more flexible and suitable for a diverse range of target audiences.