Ein Build Variant in der Android-Entwicklung ist eine Kombination aus einem build type und einem product flavor, die bestimmt, wie eine APK oder AAB erstellt wird: mit welchen Parametern, Ressourcen und Code. Jede Build-Variante stellt eine separate Gradle-Konfiguration mit eigener applicationId, Signierschlüsseln und eingebundenen Abhängigkeiten dar. Laut Google Android Developers, 2025 reduziert die richtige Konfiguration von Build Variants die Build-Zeit um bis zu 40%, indem unnötige Ressourcen für jede Variante ausgeschlossen werden. Das Build-Varianten-System ist die Grundlage des Konfigurationsmanagements in modernen Android-Projekten.
Wichtige Erkenntnisse
Build Variant ist das Ergebnis der Kombination eines Build Type mit einem Product Flavor. Wenn im Projekt keine Product Flavors definiert sind, stimmt der Build Variant mit dem Build Type überein. Gradle generiert automatisch den vollständigen Satz von Varianten als kartesisches Produkt aller FlavorDimensions, Product Flavors und Build Types. Zum Beispiel werden für die Flavors Free/Paid und die Typen Debug/Release 4 Varianten erstellt: FreeDebug, FreeRelease, PaidDebug, PaidRelease.
Jeder Build Variant erhält seinen eigenen Namen im Format <Flavor><Type> mit dem Flavor in Großbuchstaben. Gradle generiert separate Aufgaben für diese Variante: assembleFreeDebug, installFreeDebug, bundleFreeRelease. In Android Studio ist das Umschalten zwischen Varianten über das Build Variants-Panel (View → Tool Windows → Build Variants) verfügbar. Die Auswahl einer Variante beeinflusst, welcher Code kompiliert wird, welche Ressourcen eingebunden werden und welche APK/AAB erzeugt wird.
Das Build Variants-System löst drei Hauptaufgaben: Trennung von Konfigurationen für verschiedene Umgebungen (Dev/Staging/Production), Erstellung mehrerer Versionen einer App (Free/Paid) und A/B-Testing von Builds. Ohne Build Variants müssten Entwickler manuell Flags und Konfigurationen umschalten, was zu menschlichen Fehlern führt. Laut einer Studie von Gradle Inc., 2024 reduziert die Einführung von Build Variants Build-Fehler um 60% in Projekten mit drei oder mehr Deployment-Umgebungen.
AGP (Android Gradle Plugin) berechnet alle Kombinationen in der Konfigurationsphase. Wenn ein Projekt zwei Dimensionen mit zwei bzw. drei Flavors hat, erstellt Gradle 2 × 2 × 3 = 12 Kombinationen, multipliziert mit der Anzahl der Build Types (normalerweise 2). Jede Kombination erhält einen eindeutigen Namen und eine Reihe von Aufgaben. AGP fügt automatisch ein Source Set für jede Variante hinzu: src/freeDebug/, src/paidRelease/, sowie die generalisierten src/free/ und src/debug/. Priorität beim Lesen von Ressourcen: Variant → Flavor → Type → Main.
// Beispiel: 4 Build Variants
// flavorDimensions "version", "server"
// version: demo, prod
// server: mock, live
// buildTypes: debug, release
// Gesamt: 2 × 2 × 2 = 8 Varianten
android {
flavorDimensions "version", "server"
productFlavors {
demo { dimension "version" }
prod { dimension "version" }
mock { dimension "server" }
live { dimension "server" }
}
}
Build Type definiert, wie die Anwendung gebaut wird — mit oder ohne Debug-Informationen, mit oder ohne Optimierung, mit welcher Signierung. Product Flavor definiert, was gebaut wird — welche Version des Produkts. Build Type ist ein Build-Mechanismus (Debug, Release, Staging). Product Flavor ist eine Produktvariante (Free, Paid, Enterprise, Demo). Beide Konzepte sind orthogonal: Jeder Build Type kann auf jeden Product Flavor angewendet werden.
Standard-Build Types umfassen Debug (debuggable=true, minification=false, signing=debug.keystore) und Release (debuggable=false, minification=true, signing=production.keystore). Der Standard-Product Flavor ist einer, unbenannt (effektiv das Main Source Set). Entwickler können eigene Build Types (z.B. „Staging“ mit debuggable=true und minification=true) und beliebig viele Product Flavors hinzufügen. Ein weiterer Unterschied ist, dass Build Types nicht in Dimensionen gruppiert werden können, Product Flavors jedoch schon.
Der wichtigste praktische Unterschied: defaultConfig in build.gradle gilt für alle Variants, kann aber in productFlavors und buildTypes überschrieben werden. Ein zu einem buildType hinzugefügtes BuildConfigField ist in allen Flavors dieses Typs sichtbar, während ein zu einem productFlavor hinzugefügtes in allen Typen dieses Flavors sichtbar ist. Wenn ein Feld in beiden definiert ist, hat buildType Priorität (es wird zuletzt in der Kette angewendet).
| Eigenschaft | Build Type | Product Flavor |
|---|---|---|
| Zweck | Wie bauen | Was bauen |
| Beispiele | debug, release, staging | free, paid, demo, enterprise |
| Standard | debug + release | einer (main) |
| Source Set | src/debug/, src/release/ | src/free/, src/paid/ |
| Dimensionen | nein | flavorDimensions |
| Anwendungsreihenfolge | nach Flavor, überschreibt | nach defaultConfig |
| BuildConfigField | überschreibt Flavor | überschreibt defaultConfig |
Die Konfiguration von Build Variants erfolgt im android-Block der build.gradle-Datei auf Modulebene. Zuerst werden buildTypes mit ihren Parametern deklariert, dann flavorDimensions und productFlavors. Gradle erstellt automatisch Varianten basierend auf diesen Deklarationen. Jede Variante erbt den defaultConfig des Moduls und überschreibt bestimmte Felder. Die Deklarationsreihenfolge beeinflusst die Priorität: buildTypes werden nach productFlavors angewendet.
Um auf einen bestimmten Build Variant in Gradle-Skripten zuzugreifen, verwenden Sie android.applicationVariants (für App-Module) oder android.libraryVariants (für Bibliotheksmodule). Dies ist eine Sammlung, die durchlaufen werden kann, um die Konfiguration jeder Variante zur Konfigurationslaufzeit zu ändern. Beispielsweise können Sie programmatisch buildConfigField für alle Varianten hinzufügen, die das Wort „Demo“ enthalten.
Android Gradle Plugin 8.x hat Unterstützung für onVariants hinzugefügt — eine sauberere API zum Konfigurieren von Varianten über Lambdas. Die alte API (variantOutput, variantFilter) ist als veraltet markiert. Es wird empfohlen, onVariants zusammen mit onEach für Bibliotheksmodule zu verwenden. Die Migration von variantOutput zu onVariants ist ein empfohlener Schritt beim Upgrade von AGP 7.x auf 8.x.
android {
buildTypes {
debug {
debuggable true
minification false
signingConfig signingConfigs.debug
}
release {
debuggable false
minification true
proguardFiles "proguard-rules.pro"
signingConfig signingConfigs.release
}
staging {
debuggable true
minification true
versionNameSuffix "-staging"
}
}
flavorDimensions "tier", "region"
productFlavors {
free { dimension "tier" }
paid { dimension "tier" }
us { dimension "region" }
eu { dimension "region" }
}
}
android.onVariants { variant ->
if (variant.name.contains("Demo")) {
variant.setEnabled(false)
}
}
Jeder Build Variant erhält seine eigene Hierarchie von Source Sets — Verzeichnisse mit Quellcode, Ressourcen und Manifest. Ein Source Set befindet sich unter src/<variantName>/ (z.B. src/freeDebug/) und kann java/, res/, AndroidManifest.xml, assets/ enthalten. Wenn eine Datei im Source Set der Variante existiert, überschreibt sie die gleichnamige Datei aus dem Haupt-Source Set (src/main/). Bei Ressourcen findet eine Zusammenführung statt, kein Ersatz — das System führt Ressourcen aus allen aktiven Source Sets zusammen und gibt variantenspezifischen Ressourcen Priorität.
Die Source Sets für einen Build Variant werden in einer Kette aufgebaut: src/main/ → src/flavor/ → src/type/ → src/flavorType/. Zum Beispiel wird für paidRelease zuerst main angewendet, dann paid, dann release, dann paidRelease. Jedes nachfolgende Source Set überschreibt das vorherige. Das bedeutet, dass src/release/res/values/strings.xml dieselben Strings aus src/paid/ überschreibt, aber src/paidRelease/res/ hat noch höhere Priorität.
Die Verwendung von Source Sets für Varianten ist der empfohlene Weg, Ressourcen anzupassen. Anstatt BuildConfig.FLAVOR im Code zu überprüfen und die Logik zu verzweigen, können Sie einfach verschiedene Dateien in verschiedene Source Sets legen. Zum Beispiel kommen Icons für Free- und Paid-Versionen in src/free/res/ bzw. src/paid/res/, und das AndroidManifest mit unterschiedlichen Berechtigungen in src/free/AndroidManifest.xml und src/paid/AndroidManifest.xml. Dies ist sauberer, schneller (Ressourcen werden kompiliert, nicht zur Laufzeit geprüft) und sicherer (Sie können nicht versehentlich kostenpflichtige Funktionen aufgrund eines Code-Fehlers in die kostenlose Version einbauen).
In Multi-Modul-Projekten kann jedes Modul (Bibliothek) seine eigenen Build Variants haben. AGP synchronisiert Varianten automatisch: Wenn das App-Modul paidRelease baut, werden alle abhängigen Bibliotheken ebenfalls in ihren paidRelease-entsprechenden Varianten gebaut. Ein Problem entsteht, wenn eine Bibliothek keine Product Flavors hat, das App-Modul jedoch schon — dann wird die Bibliothek einmal gebaut (Release oder Debug, je nach Typ).
Für Bibliotheksmodule stimmt der Build Variant standardmäßig mit dem Build Type des App-Moduls überein, da Bibliotheken keine Product Flavors haben. Wenn eine Bibliothek sich an den Flavor des App-Moduls anpassen muss, müssen dieselben flavorDimensions und productFlavors in der Bibliothek deklariert werden. AGP gleicht Flavors durch exakte Namensübereinstimmung ab. Gradle empfiehlt, Flavors über die Build-Konfiguration im Root-Projekt mit subprojects oder Convention Plugins zu synchronisieren.
Ab AGP 8.1 können Bibliotheken multiple Variants veröffentlichen — alle Bibliotheksvarianten gleichzeitig in einem Maven-Repository veröffentlichen. Dies löst das Problem, wenn das App-Modul einen kostenpflichtigen Flavor verwendet, die Bibliothek aber nur für Free veröffentlicht ist. Multiple Variants Publishing (MVP) ermöglicht es dem abhängigen Projekt, die benötigte Variante automatisch auszuwählen. Um MVP zu aktivieren, fügen Sie publishing { multipleVariants { ... } } zur build.gradle der Bibliothek hinzu.
Manchmal ist es notwendig, einige Build Variants zu deaktivieren — zum Beispiel, wenn die Kombination mockRelease keinen Sinn ergibt (Mock-Server sollte nicht in Produktion gehen). Gradle stellt variantFilter zur Verfügung — einen DSL-Block, in dem Sie die Eigenschaften jeder Variante überprüfen und sie über setIgnore(true) deaktivieren können. VariantFilter wird in der Konfigurationsphase vor der Aufgabenerstellung angewendet, sodass eine deaktivierte Variante keine Assemble- und Install-Aufgaben generiert.
Filtern ist auch nützlich, um Builds zu beschleunigen. Wenn ein Projekt 8 Varianten hat, ein Entwickler aber nur an einer arbeitet, durchlaufen die restlichen 7 Varianten trotzdem die Konfiguration. Bei Verwendung von variantFilter erstellen deaktivierte Varianten keine Aufgaben, was die Konfigurationszeit bei Projekten mit 6+ Flavor-Dimensionen um 30-50% reduziert. In CI/CD können Sie Varianten dynamisch über Kommandozeilenparameter -PbuildOnly=paidRelease filtern.
android {
variantFilter { variant ->
// Mock für Release und Demo für Production deaktivieren
def names = variant.flavors*.name
def isMock = names.contains("mock")
def isDemo = names.contains("demo")
def isRelease = variant.buildType.name == "release"
if ((isMock && isRelease) || (isDemo && !isMock)) {
variant.setIgnore(true)
}
}
}
// Dynamisches Filtern über Parameter
if (project.hasProperty("buildOnly")) {
def target = project.property("buildOnly")
android.variantFilter { variant ->
variant.setIgnore(variant.name != target)
}
}
Häufig gestellte Fragen
Es gibt keine Grenze, aber Gradle erstellt das kartesische Produkt aller Flavors und Typen. Wenn Sie 3 Dimensionen mit jeweils 3 Flavors und 3 Build Types haben, erhalten Sie 27 Varianten. Zu viele Varianten verlangsamen die Konfiguration. Es wird empfohlen, nicht mehr als 10–12 Varianten in einem Modul zu haben.
flavorDimensions gruppieren Product Flavors in unabhängige Achsen. Zum Beispiel die Dimension „Tier“ (Free, Paid) und die Dimension „Region“ (US, EU). Ohne Dimensionen gehören alle Flavors zu einer Achse, und Gradle wählt nur einen Flavor aus allen aus (Sie können Free+US und Paid+EU nicht als separate Varianten haben).
Geben Sie im productFlavor- oder buildType-Block applicationId an. Zum Beispiel für die kostenlose Version: free { applicationId „com.example.app.free“ }. Verwenden Sie im Manifest ${applicationId} — Gradle setzt den Wert automatisch ein. Dadurch können beide Varianten auf einem Gerät installiert werden.
In iOS ist das Äquivalent zu Build Variants die Kombination aus Scheme + Configuration. Xcode Schemes werden über Debug/Release-Konfigurationen mit verschiedenen Parametern eingerichtet. Für mehrere Versionen (Free/Paid) werden Build Configurations und Preprocessor Macros verwendet. In Android ist das Konzept formalisierter und in Gradle integriert.
Ja, jede Variante kann eine andere APK-Größe haben. Debug-Builds enthalten Debug-Informationen, SDK und nicht unterstützte Ressourcen. Release-Builds mit Minification und Resource Shrinking erzeugen die minimale Größe. Der Product Flavor beeinflusst ebenfalls die Größe: Eine kostenlose Version ohne kostenpflichtige Bibliotheken ist um die Größe dieser Bibliotheken kleiner als die kostenpflichtige Version.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch