Gradle Product Flavors for Multi‑Channel Android Packaging
This article explains how to use Gradle's productFlavors, flavorDimensions, variant filters, signing configurations, manifest placeholders and source‑set priorities to create flexible multi‑channel Android builds, comparing fast simple packaging with a fully customizable approach and providing command‑line examples.
When building Android apps you often need to generate multiple channel packages. Two main strategies exist: a quick solution that only changes a channel identifier, and a more flexible approach using Gradle's productFlavors to customize code, resources, dependencies, and signing per channel.
Configuration : In the module's build.gradle define a flavor dimension and product flavors, e.g.:
android {
flavorDimensions "channel"
productFlavors {
common {
dimension "channel"
}
xiaomi {
dimension "channel"
minSdkVersion 21
versionCode 20000 + defaultConfig.versionCode
versionNameSuffix "-minApi21"
}
huawei {
dimension "channel"
minSdkVersion 23
versionCode 20000 + defaultConfig.versionCode
versionNameSuffix "-minApi23"
}
}
buildTypes {
debug { minifyEnabled false }
release { minifyEnabled false }
}
}Gradle will generate build variants for each combination of flavor and build type, e.g. app-[common,xiaomi,huawei]-[debug,release].apk .
Variant Filtering : Use variantFilter to exclude unwanted variants, for example removing commonDebug :
android {
variantFilter { variant ->
def names = variant.flavors*.name
def buildTypeName = variant.buildType.name
if (buildTypeName.contains("debug") && names.contains("common")) {
setIgnore(true)
}
}
}Dependencies : Declare channel‑specific dependencies, such as:
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:26.1.0"
xiaomiDebugImplementation "com.xxx:xxx:1.6.0"
debugImplementation "com.xxx:xxx:1.6.0"
commonImplementation "com.xxx:xxx:1.6.0"
}Signing Configs : Define separate keystores for each channel and assign them in the release build type:
signingConfigs {
test11 {
storeFile file("../test11.keystore")
storePassword "test11"
keyAlias "test11"
keyPassword "test11"
}
test22 {
storeFile file("../test22.keystore")
storePassword "test22"
keyAlias "test22"
keyPassword "test22"
}
}
android {
buildTypes {
release {
productFlavors.huawei.signingConfig signingConfigs.test11
productFlavors.xiaomi.signingConfig signingConfigs.test22
}
}
}Manifest Placeholders : Use manifestPlaceholders to replace icons, activity names, and meta‑data per flavor:
productFlavors {
common {
manifestPlaceholders = [
ChannelData: "Common Meta Data",
AppIcon: "@mipmap/ic_common",
MainActivity: "CommonActivity"
]
}
xiaomi {
manifestPlaceholders = [
ChannelData: "XiaoMiMetaData",
AppIcon: "@mipmap/ic_launcher",
MainActivity: "XMActivity"
]
}
huawei {
manifestPlaceholders = [
ChannelData: "HuaWeiMetaData",
AppIcon: "@mipmap/ic_launcher",
MainActivity: "HWActivity"
]
}
}Source‑Set Priority : Gradle merges resources and code from source sets in the order commonDebug → debug → common → main . Files in higher‑priority sets override those in lower ones, allowing channel‑specific assets, strings, and Java classes without duplication errors.
Command‑Line Build : Common Gradle commands include gradle assemble (all), gradle assembleDebug , gradle assembleRelease , gradle assembleXiaomiDebug , and gradle installXiaomiDebug . Use gradle clean before testing.
For further reading see the official Android documentation and related blog posts.
Qunar Tech Salon
Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.