How does AGP compute the build variant matrix from build types and product flavors, and what tasks/source sets does each variant produce?
answer
- variant = flavors-per-dimension × build type
- multiplicative growth
- assemble<Variant> + per-variant source set
- beforeVariants / variantFilter to prune
- androidComponents Variant API over legacy applicationVariants
basics
~20 sA variant is one combination: (one flavor per dimension) × (one build type). AGP multiplies them — e.g. 2 flavors × 2 build types = 4 variants — and generates a source set and assemble<Variant> task for each.
solid answer
~40 sAGP builds the **variant matrix** as the Cartesian product of *(one flavor from each flavor dimension)* and *(each build type)*. With flavors `free`/`paid` and build types `debug`/`release` you get four variants: `freeDebug`, `freeRelease`, `paidDebug`, `paidRelease`. Each variant materializes as: a merged source set chain (`main` → flavor(s) → flavor-combo → buildType → full combo), generated tasks (`assembleFreeDebug`, `installFreeDebug`, `testFreeDebugUnitTest`, etc.), its own `BuildConfig`, and its own merged manifest/resources. The variant count grows multiplicatively, which is why teams use `variantFilter`/`androidComponents.beforeVariants` to prune impossible combinations (e.g. never build `freeRelease`). Variants are the unit AGP actually compiles and packages; flavors and build types are just the inputs to the matrix.
code
kotlin · 9 linesandroidComponents {
beforeVariants { variant ->
// Drop the free + release combination entirely
if (variant.productFlavors.any { it.second == "free" } &&
variant.buildType == "release") {
variant.enable = false
}
}
}go deeper
Know a variant is a flavor+buildType combo and produces an assemble task.
Compute the matrix, list generated tasks/source sets, and mention pruning with beforeVariants/variantFilter.
Discuss combinatorial explosion, the androidComponents Variant API, configuration-cache implications, and merge-priority subtleties.
Set org policy on variant proliferation, CI build-time budgets per variant, and standardizing on the supported Variant API across teams.
## What a variant is A **build variant** is the concrete, buildable artifact AGP produces. It is exactly one **build type** combined with exactly one **product flavor per dimension**. If you have no flavors, the variant *is* just the build type (`debug`, `release`). ## The matrix formula ``` variants = (flavors in dim1) × (flavors in dim2) × … × (build types) ``` Examples: - No flavors, types {debug, release} → **2** variants. - tier{free,paid} × types{debug,release} → **4** variants. - tier{free,paid} × env{staging,prod} × types{debug,release} → **8** variants. This multiplicative growth is the classic *combinatorial explosion* — eight flavors across two dimensions with three build types is 48 variants, each with its own task chain. ## What each variant generates For variant `freeProdRelease` AGP creates: - **Source-set merge chain** (low→high priority): `src/main` → `src/free` → `src/prod` → `src/freeProd` → `src/release` → `src/freeProdRelease`. Resources and manifests overlay/merge; Java/Kotlin sources are additive. - **Tasks**: `assembleFreeProdRelease`, `bundleFreeProdRelease` (AAB), `installFreeProdRelease`, `testFreeProdReleaseUnitTest`, `connectedFreeProdReleaseAndroidTest`, plus `compile…`, `merge…Resources`, `process…Manifest`. - **A generated `BuildConfig`** with the merged fields and `BuildConfig.BUILD_TYPE`, `BuildConfig.FLAVOR`. - A **mergeable dependency graph**: variant-specific configurations (`freeProdReleaseImplementation` is resolvable). ## Pruning the matrix Two idiomatic ways to drop unwanted combinations: ```kotlin androidComponents { beforeVariants { variant -> // never build the free tier in release if (variant.flavorName == "free" && variant.buildType == "release") { variant.enable = false } } } ``` (The legacy `android.variantFilter { ... }` does the same in older AGP.) ## Why this matters in interviews Variants are the real unit of work; misunderstanding the matrix leads people to think each task is independent when they share source sets and configs. The `androidComponents` **Variant API** (AGP 7+/8.x) is the supported, configuration-cache-friendly way to read/modify variants, replacing the deprecated mutable `applicationVariants` DSL. ## Gradle-core boundary The entire matrix is AGP machinery; Gradle core supplies the task graph, configurations, and source sets AGP wires together. For deep variant-API details, bridge to the Android/AGP docs.
- If you have two flavor dimensions of 3 and 2 flavors plus debug/release, how many variants?3 × 2 × 2 = 12 variants.
- Why is `androidComponents { beforeVariants { } }` preferred over the old `applicationVariants` DSL?It's the supported AGP 7+/8.x Variant API: lazy, configuration-cache compatible, and lets you disable/read variants without eagerly realizing them, whereas the legacy mutable DSL is deprecated and CC-hostile.
- What is the source-set merge priority order for a variant?main (lowest) → product flavors in dimension order → flavor combination → build type → full variant combination (highest), with higher priority overriding on conflicts.
The variant matrix is a spreadsheet: flavor dimensions are columns, build types are rows, and every filled cell is one variant you can build.
saying these in an interview costs you the question
- Saying variants add rather than multiply
- Modifying variants via the deprecated `applicationVariants` instead of `androidComponents`
- Forgetting that no-flavor projects still have variants (just the build types)