Build Variant — build type và product flavor trong Android là gì

Tác giả: IT Sectr Đã đăng: 2026-05-30 Thời gian đọc: 9 phút

Build Variant trong phát triển Android là sự kết hợp của một build type và một product flavor, quyết định cách APK hoặc AAB sẽ được xây dựng: với những tham số, tài nguyên và mã nguồn nào. Mỗi biến thể build đại diện cho một cấu hình Gradle riêng với applicationId, khóa ký và các phụ thuộc riêng. Theo Google Android Developers, 2025, cấu hình đúng cách Build Variants giúp giảm thời gian build tới 40% nhờ loại bỏ tài nguyên không cần thiết cho mỗi biến thể. Hệ thống biến thể build là nền tảng của quản lý cấu hình trong các dự án Android hiện đại.

Điểm Chính

  • Build Variant — sự kết hợp của một Build Type và một Product Flavor.
  • Build Type xác định chế độ build: debug hoặc release.
  • Product Flavor xác định phiên bản ứng dụng: free, paid, demo, enterprise.
  • Gradle tự động tạo các tác vụ cho mỗi Build Variant, bao gồm install và assemble.
  • Tài nguyên và mã nguồn có thể được ghi đè cho mỗi biến thể thông qua các source sets tương ứng.

Build Variant là gì?

Build Variant là kết quả của việc kết hợp một Build Type và một Product Flavor. Nếu không có Product Flavors nào được xác định trong dự án, Build Variant sẽ trùng với Build Type. Gradle tự động tạo tập hợp đầy đủ các biến thể dưới dạng tích Descartes của tất cả FlavorDimensions, Product Flavors và Build Types. Ví dụ, với flavors free/paid và types debug/release, sẽ có 4 biến thể được tạo: freeDebug, freeRelease, paidDebug, paidRelease.

Mỗi Build Variant nhận tên riêng theo định dạng <Flavor><Type> với flavor được viết hoa. Gradle tạo các tác vụ riêng cho biến thể này: assembleFreeDebug, installFreeDebug, bundleFreeRelease. Trong Android Studio, việc chuyển đổi giữa các biến thể có sẵn qua bảng Build Variants (View → Tool Windows → Build Variants). Việc chọn biến thể ảnh hưởng đến mã nào được biên dịch, tài nguyên nào được bao gồm và APK/AAB nào được tạo ra.

Hệ thống Build Variants giải quyết ba nhiệm vụ chính: tách biệt cấu hình cho các môi trường khác nhau (dev/staging/production), tạo nhiều phiên bản ứng dụng (free/paid) và kiểm thử A/B các bản build. Nếu không có Build Variants, các nhà phát triển phải thủ công chuyển đổi cờ và cấu hình, dẫn đến lỗi do yếu tố con người. Theo nghiên cứu của Gradle Inc., 2024, việc áp dụng Build Variants giảm 60% lỗi build trong các dự án có ba môi trường triển khai trở lên.

Cách Gradle tạo các biến thể

AGP (Android Gradle Plugin) tính toán tất cả các tổ hợp ở giai đoạn cấu hình. Nếu một dự án có hai chiều với lần lượt hai và ba flavors, Gradle sẽ tạo 2 × 2 × 3 = 12 tổ hợp, nhân với số lượng Build Types (thường là 2). Mỗi tổ hợp nhận một tên duy nhất và một tập hợp các tác vụ. AGP tự động thêm source set cho mỗi biến thể: src/freeDebug/, src/paidRelease/, cũng như các source set tổng quát src/free/src/debug/. Thứ tự ưu tiên đọc tài nguyên: variant → flavor → type → main.

groovy
// Ví dụ: 4 Build Variants
// flavorDimensions "version", "server"
// version: demo, prod
// server: mock, live
// buildTypes: debug, release
// Tổng: 2 × 2 × 2 = 8 biến thể

android {
    flavorDimensions "version", "server"

    productFlavors {
        demo { dimension "version" }
        prod { dimension "version" }
        mock { dimension "server" }
        live { dimension "server" }
    }
}

Build Type và Product Flavor: Khác biệt

Build Type xác định cách xây dựng ứng dụng — có hoặc không có thông tin gỡ lỗi, có hoặc không tối ưu hóa, với chữ ký nào. Product Flavor xác định xây dựng cái gì — phiên bản nào của sản phẩm. Build Type là cơ chế build (debug, release, staging). Product Flavor là biến thể sản phẩm (free, paid, enterprise, demo). Cả hai khái niệm đều trực giao: bất kỳ Build Type nào cũng có thể áp dụng cho bất kỳ Product Flavor nào.

Các Build Types mặc định bao gồm debug (debuggable=true, minification=false, signing=debug.keystore) và release (debuggable=false, minification=true, signing=production.keystore). Product Flavor mặc định là một, không tên (thực chất là main source set). Các nhà phát triển có thể thêm Build Types riêng (ví dụ, “staging” với debuggable=true và minification=true) và bất kỳ số lượng Product Flavors nào. Một khác biệt khác là Build Types không thể được nhóm thành các chiều, nhưng Product Flavors thì có thể.

Khác biệt thực tế chính: defaultConfig trong build.gradle áp dụng cho tất cả Variants nhưng có thể được ghi đè trong productFlavors và buildTypes. BuildConfigField được thêm vào buildType hiển thị trong tất cả flavors của loại đó, trong khi thêm vào productFlavor hiển thị trong tất cả loại của flavor đó. Nếu một trường được xác định ở cả hai, buildType được ưu tiên (nó được áp dụng cuối cùng trong chuỗi).

Bảng So sánh

Đặc tínhBuild TypeProduct Flavor
Mục đíchCách xây dựngXây dựng cái gì
Ví dụdebug, release, stagingfree, paid, demo, enterprise
Mặc địnhdebug + releasemột (main)
Source Setsrc/debug/, src/release/src/free/, src/paid/
ChiềukhôngflavorDimensions
Thứ tự áp dụngsau flavor, ghi đèsau defaultConfig
BuildConfigFieldghi đè flavorghi đè defaultConfig

Cấu hình Build Variants trong build.gradle

Ưu tiên Cấu hình

Việc cấu hình Build Variants được thực hiện trong khối android của tệp build.gradle ở cấp mô-đun. Đầu tiên, các buildTypes được khai báo với tham số của chúng, sau đó là flavorDimensions và productFlavors. Gradle tự động tạo các biến thể dựa trên các khai báo này. Mỗi biến thể kế thừa defaultConfig của mô-đun, ghi đè các trường được chỉ định. Thứ tự khai báo ảnh hưởng đến ưu tiên: buildTypes được áp dụng sau productFlavors.

Để truy cập một Build Variant cụ thể trong tập lệnh Gradle, sử dụng android.applicationVariants (cho mô-đun app) hoặc android.libraryVariants (cho mô-đun thư viện). Đây là một tập hợp có thể duyệt để sửa đổi cấu hình của mỗi biến thể tại thời điểm chạy cấu hình. Ví dụ, bạn có thể thêm buildConfigField theo chương trình cho tất cả các biến thể chứa từ “demo”.

Android Gradle Plugin 8.x đã thêm hỗ trợ cho onVariants — một API sạch hơn để cấu hình các biến thể thông qua lambdas. API cũ (variantOutput, variantFilter) đã được đánh dấu là không dùng nữa. Nên sử dụng onVariants cùng với onEach cho các mô-đun thư viện. Di chuyển từ variantOutput sang onVariants là bước được khuyến nghị khi nâng cấp AGP từ 7.x lên 8.x.

groovy
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)
    }
}

Source Sets và Ghi đè Tài nguyên

Mỗi Build Variant nhận một hệ thống phân cấp source sets riêng — các thư mục chứa mã nguồn, tài nguyên và tệp kê khai. Một source set nằm tại src/<variantName>/ (ví dụ, src/freeDebug/) và có thể chứa java/, res/, AndroidManifest.xml, assets/. Nếu một tệp tồn tại trong source set của biến thể, nó sẽ ghi đè tệp cùng tên từ source set chính (src/main/). Đối với tài nguyên, việc hợp nhất diễn ra thay vì thay thế — hệ thống hợp nhất tài nguyên từ tất cả các source sets đang hoạt động, ưu tiên các tài nguyên dành riêng cho biến thể.

Các source sets cho một Build Variant được xây dựng theo chuỗi: src/main/src/flavor/src/type/src/flavorType/. Ví dụ, đối với paidRelease, main được áp dụng trước, sau đó là paid, rồi release, rồi paidRelease. Mỗi source set tiếp theo sẽ ghi đè source set trước đó. Điều này có nghĩa là src/release/res/values/strings.xml sẽ ghi đè các chuỗi tương tự từ src/paid/, nhưng src/paidRelease/res/ có ưu tiên cao hơn.

Sử dụng source sets cho các biến thể là cách được khuyến nghị để tùy chỉnh tài nguyên. Thay vì kiểm tra BuildConfig.FLAVOR trong mã và rẽ nhánh logic, bạn chỉ cần đặt các tệp khác nhau vào các source sets khác nhau. Ví dụ, biểu tượng cho các phiên bản free và paid được đặt trong src/free/res/src/paid/res/ tương ứng, và AndroidManifest với các quyền khác nhau được đặt trong src/free/AndroidManifest.xmlsrc/paid/AndroidManifest.xml. Cách này sạch hơn, nhanh hơn (tài nguyên được biên dịch, không kiểm tra khi chạy) và an toàn hơn (bạn không thể vô tình đưa chức năng trả phí vào phiên bản miễn phí do lỗi trong mã).

Build Variant trong Dự án Đa Mô-đun

Trong các dự án đa mô-đun, mỗi mô-đun (thư viện) có thể có Build Variants riêng. AGP tự động đồng bộ hóa các biến thể: nếu mô-đun app xây dựng paidRelease, tất cả các thư viện phụ thuộc cũng được xây dựng trong các biến thể tương ứng với paidRelease. Vấn đề phát sinh khi một thư viện không có product flavors nhưng mô-đun app có — khi đó thư viện được xây dựng một lần (release hoặc debug tùy theo loại).

Đối với các mô-đun thư viện, Build Variant mặc định trùng với Build Type của mô-đun app, vì các thư viện không có product flavors. Nếu một thư viện cần thích ứng với flavor của mô-đun app, các flavorDimensions và productFlavors tương tự phải được khai báo trong thư viện. AGP so khớp flavors bằng tên chính xác. Gradle khuyến nghị đồng bộ hóa flavors thông qua cấu hình build trong dự án gốc bằng cách sử dụng subprojects hoặc Convention Plugins.

Bắt đầu từ AGP 8.1, các thư viện có thể xuất bản nhiều biến thể — xuất bản tất cả các biến thể thư viện vào một kho lưu trữ maven cùng một lúc. Điều này giải quyết vấn đề khi mô-đun app sử dụng flavor trả phí nhưng thư viện chỉ được xuất bản cho miễn phí. Multiple variants publishing (MVP) cho phép dự án phụ thuộc tự động chọn biến thể cần thiết. Để bật MVP, thêm publishing { multipleVariants { ... } } vào build.gradle của thư viện.

Lọc và Vô hiệu hóa Biến thể

Lọc Động qua CI/CD

Đôi khi cần vô hiệu hóa một số Build Variants — ví dụ, nếu tổ hợp mockRelease không có ý nghĩa (máy chủ mock không nên đi vào môi trường production). Gradle cung cấp variantFilter — một khối DSL nơi bạn có thể kiểm tra các thuộc tính của mỗi biến thể và vô hiệu hóa nó qua setIgnore(true). VariantFilter được áp dụng ở giai đoạn cấu hình, trước khi tạo tác vụ, do đó biến thể bị vô hiệu hóa sẽ không tạo các tác vụ assemble và install.

Việc lọc cũng hữu ích để tăng tốc độ build. Nếu một dự án có 8 biến thể nhưng nhà phát triển chỉ làm việc trên một, 7 biến thể còn lại vẫn phải trải qua cấu hình. Khi sử dụng variantFilter, các biến thể bị vô hiệu hóa không tạo tác vụ, giảm thời gian cấu hình xuống 30-50% cho các dự án có 6+ chiều flavor. Trong CI/CD, bạn có thể lọc động các biến thể qua tham số dòng lệnh -PbuildOnly=paidRelease.

groovy
android {
    variantFilter { variant ->
        // Vô hiệu hóa mock cho release và demo cho production
        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)
        }
    }
}

// Lọc động qua tham số
if (project.hasProperty("buildOnly")) {
    def target = project.property("buildOnly")
    android.variantFilter { variant ->
        variant.setIgnore(variant.name != target)
    }
}

Câu hỏi Thường gặp

Có thể tạo bao nhiêu Build Variants?

Không có giới hạn, nhưng Gradle tạo tích Descartes của tất cả các flavor và loại. Nếu bạn có 3 chiều với mỗi chiều 3 flavor và 3 build types, bạn sẽ có 27 biến thể. Quá nhiều biến thể làm chậm cấu hình. Nên không quá 10–12 biến thể trong một mô-đun.

Tại sao cần flavorDimensions?

flavorDimensions nhóm các Product Flavors thành các trục độc lập. Ví dụ, chiều “tier” (free, paid) và chiều “region” (us, eu). Không có chiều, tất cả các flavor thuộc về một trục và Gradle sẽ chỉ chọn một flavor từ tất cả (bạn không thể có free+us và paid+eu là các biến thể riêng biệt).

Làm thế nào để ghi đè applicationId cho một biến thể?

Trong khối productFlavor hoặc buildType, chỉ định applicationId. Ví dụ, cho phiên bản miễn phí: free { applicationId “com.example.app.free” }. Trong tệp kê khai, sử dụng ${applicationId} — Gradle sẽ tự động thay thế giá trị. Điều này cho phép cài đặt cả hai biến thể trên một thiết bị.

Có thể sử dụng Build Variants trong iOS không?

Trong iOS, tương đương với Build Variants là sự kết hợp của Scheme + Configuration. Các Xcode Schemes được cấu hình thông qua các cấu hình Debug/Release với các tham số khác nhau. Đối với nhiều phiên bản (free/paid), Build Configurations và Preprocessor Macros được sử dụng. Trên Android, khái niệm này được chính thức hóa hơn và tích hợp sẵn trong Gradle.

Build Variant có ảnh hưởng đến kích thước APK không?

Có, mỗi biến thể có thể có kích thước APK khác nhau. Các bản build debug bao gồm thông tin gỡ lỗi, SDK và tài nguyên không được hỗ trợ. Các bản build release với minification và resource shrinking tạo ra kích thước tối thiểu. Product Flavor cũng ảnh hưởng đến kích thước: phiên bản miễn phí không có thư viện trả phí sẽ nhỏ hơn phiên bản trả phí bằng kích thước của các thư viện đó.

Tổng kết

  • Build Variant — sự kết hợp của một Build Type và một Product Flavor xác định cấu hình build.
  • Build Type kiểm soát chế độ biên dịch (debug/release/staging), còn Product Flavor kiểm soát phiên bản sản phẩm (free/paid).
  • Source sets cho phép ghi đè mã, tài nguyên và tệp kê khai cho mỗi biến thể build.
  • VariantFilter vô hiệu hóa các tổ hợp không cần thiết, tăng tốc cấu hình Gradle lên 30–50%.
  • Dự án đa mô-đun yêu cầu đồng bộ hóa flavor trên tất cả các mô-đun hoặc xuất bản nhiều biến thể.
  • BuildConfigField và source sets là hai cách sạch để tùy chỉnh hành vi giữa các biến thể.
  • Khuyến nghị: không tạo quá 10–12 biến thể trong một dự án; nhóm các chiều một cách có ý nghĩa.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm