Build Variant ในการพัฒนา Android คือการรวมกันของ build type และ product flavor ซึ่งกำหนดว่า APK หรือ AAB จะถูกสร้างอย่างไร: ด้วยพารามิเตอร์ ทรัพยากร และโค้ดใดบ้าง แต่ละ build variant แสดงค่าการกำหนดค่า Gradle แยกต่างหากแยกกับ applicationId ของตนเอง คีย์ลงนาม และโดยประกอบที่เพิ่มเข้ามา ตามแหล่งข้อมูล Google Android Developers, 2025 การกำหนดค่า Build Variants อย่างถูกต้องช่วยลดเวลา build ได้มากถึง 40% โดยการยกเว้นทรัพยากรที่ไม่จำเป็นสำหรับแต่ละ variant ระบบ build variant เป็นรากฐานของการจัดการกำหนดค่าในโปรเจกต์ Android สมัยใหม่
ข้อสำคัญ
Build Variant คือผลลัพธ์ของการรวมหนึ่ง Build Type และหนึ่ง Product Flavor หากไม่มีการกำหนด Product Flavor ในโปรเจกต์ Build Variant จะตรงกับ Build Type Gradle จะสร้างชุดของ variant ที่สมบูรณ์โดยอัตโนมัติเป็นผลคูณแบบคาร์เทเซียนของ FlavorDimensions, Product Flavors และ Build Types ทั้งหมด ตัวอย่างเช่น, สำหรับ free/paid flavors และ debug/release types จะมี 4 variant ถูกสร้าง: freeDebug, freeRelease, paidDebug, paidRelease
แต่ละ Build Variant ได้รับชื่อของตนเองในรูปแบบ <Flavor><Type> โดย flavor ขึ้นต้นด้วยตัวพิมพ์ใหญ่ Gradle สร้างงานแยกต่างสำหรับ variant นี้: assembleFreeDebug, installFreeDebug, bundleFreeRelease ใน Android Studio การสลับระหว่าง variant สามารถทำได้ผ่านแผง Build Variants (View → Tool Windows → Build Variants) การเลือก variant มีผลต่อโค้ดที่จะถูกคอมไพล์ ทรัพยากรที่จะถูกรวม และ APK/AAB ใดที่จะถูกผลิต
ระบบ Build Variants แก้ไขระบสามงานหลัก: แยกการกำหนดค่าสำหรับสภาพแวดล้อมที่แตกต่างกัน (dev/staging/production), สร้างหลายเวอร์ชันของแอป (free/paid) และการทดสอบ A/B ของ build หากไม่มี Build Variants นักพัฒนาจะต้องเปลี่ยนแบบ flag และการกำหนดค่าด้วยตนเอง ซึ่งนำไปสู่ข้อผิดพลาดของปัจจัยมนุษย์ ตามการศึกษาโดย Gradle Inc., 2024 การนำ Build Variants ไปใช้ช่วยลดข้อผิดพลาดของ build ได้ 60% ในโปรเจกต์ที่มีสามสภาพแวดล้อมการประการใช้งานหรือมากกว่า
AGP (Android Gradle Plugin) คำนวณการรวมกันทั้งหมดในขั้นตอนการกำหนดค่า หากโปรเจกต์มีสองมิติที่มีสองและสาม flavor ตามลำดับ Gradle จะสร้าง 2 × 2 × 3 = 12 การรวมกัน คูณด้วยจำนวน Build Types (โดยปกติ่ 2) แต่ละการรวมกันได้รับชื่อที่ไม่ซ้ำและชุดงาน AGP เพิ่ม source set โดยอัตโนมัติสำหรับแต่ละ variant: src/freeDebug/, src/paidRelease/ รวมทั้งทั้วไป src/free/ และ src/debug/ ลำดับความสำคัญในการอ่านทรัพยากร: variant → flavor → type → main
// ตัวอย่าง: 4 Build Variants
// flavorDimensions "version", "server"
// version: demo, prod
// server: mock, live
// buildTypes: debug, release
// รวม: 2 × 2 × 2 = 8 variant
android {
flavorDimensions "version", "server"
productFlavors {
demo { dimension "version" }
prod { dimension "version" }
mock { dimension "server" }
live { dimension "server" }
}
}
Build Type กำหนดวิธีการสร้างแอปลิเคชัน — มีหรือไม่มีข้อมูล debug, มีหรือไม่มีการเพิ่มเติมประสิทธิภาพ, ด้วยลายเซ็นใด Product Flavor กำหนดว่าจะสร้างอะไร — เวอร์ชันไหนของผลิตภัณฑ์ Build Type เป็นกลไกการ build (debug, release, staging) Product Flavor เป็นตัวแปรของผลิตภัณฑ์ (free, paid, enterprise, demo) ทั้งสองแนวคิดเป็นอิสระ: Build Type ใดก็สามารถใช้กับ Product Flavor ใดก็ได้
Build Types เริ่มต้นรวมถึง debug (debuggable=true, minification=false, signing=debug.keystore) และ release (debuggable=false, minification=true, signing=production.keystore) Product Flavor เริ่มต้นคือหนึ่ง ไม่มีชื่อ (โดยพฤติเป็น main source set) นักพัฒนาสามารถเพิ่ม Build Types ของตนเอง (ตัวอย่าง “staging” ที่มี debuggable=true และ minification=true) และ Product Flavors จำนวนใดก็ได้ ข้อแตกต่างอีกอย่างคือ Build Type ไม่สามารถจัดกลุ่มเป็นมิติ แต่ Product Flavor สามารถ
ข้อแตกต่างในทางปฏิบัติหลัก: defaultConfig ใน build.gradle ใช้กับทุก Variants แต่สามารถถูกแทนที่ใน productFlavors และ buildTypes BuildConfigField ที่เพิ่มใน buildType จะมองเห็นในทุก flavor ของชนิดนั้น ในขณะที่เพิ่มใน productFlavor จะมองเห็นในทุกชนิดของ flavor นั้น หากกำหนดฟิลด์ในทั้งสองที่ buildType จะได้รับความสำคัญ (ใช้เป็นตัวสุดท้ายในโบ)
| ลักษณะ | Build Type | Product Flavor |
|---|---|---|
| วัตถุประสงค์ | วิธีการ build | สิ่งที่จะ build |
| ตัวอย่าง | debug, release, staging | free, paid, demo, enterprise |
| ค่าเริ่มต้น | debug + release | หนึ่ง (main) |
| Source Set | src/debug/, src/release/ | src/free/, src/paid/ |
| มิติ | ไม่ | flavorDimensions |
| ลำดับการใช้ | หลัง flavor, แทนที่ | หลัง defaultConfig |
| BuildConfigField | แทนที่ flavor | แทนที่ defaultConfig |
การกำหนดค่า Build Variants ทำในบล็อก android ของไฟล์ build.gradle ระดับโมดูล แรกแถลง buildTypes กัพารามิเตอร์ของพวกมัน จากนั้น flavorDimensions และ productFlavors Gradle สร้าง variant โดยอัตโนมัติจากการแถลงเหล่านั้น แต่ละ variant สืบทอด defaultConfig ของโมดูล โดยแทนที่ฟิลด์ที่ระบุ ลำดับการแถลงมีผลต่อลำดับความสำคัญ: buildTypes ถูกใช้หลัง productFlavors
เพื่อเข้าถึง Build Variant เฉพาะเจาะจงในสคริปต์ Gradle ใช้ android.applicationVariants (สำหรับโมดูล app) หรือ android.libraryVariants (สำหรับโมดูลไลบรารี) นี่เป็นกลุ่มที่สามารถวนซ้ำเพื่อแก้ไขการกำหนดค่าของแต่ละ variant ในระที่การกำหนดค่าทำงาน ตัวอย่างเช่น, คุณสามารถเพิ่ม buildConfigField โดยเป็นโปรแกรมสำหรับทุก variant ที่มีคำ “demo”
Android Gradle Plugin 8.x เพิ่มการสนับสนุน onVariants — API ที่สะอาดขึ้นสำหรับการกำหนดค่า variant ผ่าน lambdas API เก่า (variantOutput, variantFilter) ถูกทำเครื่องหมายว่าเลือกใช้ไม่แนะนำให้ใช้ onVariants ร่วมกับ onEach สำหรับโมดูลไลบรารี การย้ายจาก variantOutput ไปยัง onVariants เป็นขั้นตอนที่แนะนำเมื่ออัปเกรด AGP จาก 7.x เป็น 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)
}
}
แต่ละ Build Variant ได้รับลำดับชั้นของ source sets ของตนเอง — ไดเร็กทอรีที่มีโค้ดต้นฉบับ, ทรัพยากร และแมนิเฟสต์ source set อยู่ที่ src/<variantName>/ (ตัวอย่าง src/freeDebug/) และสามารถมี java/, res/, AndroidManifest.xml, assets/ หากไฟล์มีอยู่ใน source set ของ variant ไฟล์นั้นจะแทนที่ไฟล์ชื่อเดียวกันจาก source set หลัก (src/main/) สำหรับทรัพยากร จะมีการรวมกันแทนที่จะเป็นการแทนที่ — ระบบจะรวมทรัพยากรจากทุก source sets ที่เปิดใช้งาน โดยให้ความสำคัญกับที่เฉพาะของ variant
Source sets สำหรับ Build Variant ถูกสร้างเป็นโส: src/main/ → src/flavor/ → src/type/ → src/flavorType/ ตัวอย่างเช่น, สำหรับ paidRelease ใช้ main ก่อน, จากนั้น paid, จากนั้น release, จากนั้น paidRelease แต่ละ source set ถัดไปแทนที่อันก่อนหน้า ซึ่งหมายความว่า src/release/res/values/strings.xml จะแทนที่ string เดียวกันจาก src/paid/ แต่ src/paidRelease/res/ มีความสำคัญมากกว่า
การใช้ source sets สำหรับ variant เป็นวิธีที่แนะนำในการปรับแต่งทรัพยากร แทนที่จะตรวจสอบ BuildConfig.FLAVOR ในโค้ดและแยกสาขาโลจิก คุณสามารถวางไฟล์แตกต่างกันใน source sets แตกต่างกัน ตัวอย่างเช่น, ไอคอนสำหรับรุ่น free และ paid ไปใน src/free/res/ และ src/paid/res/ ตามลำดับ และ AndroidManifest ที่มีสิทธิ์แตกต่างกันไปใน src/free/AndroidManifest.xml และ src/paid/AndroidManifest.xml วิธีนี้สะอาดกว่า, ไวกว่า (ทรัพยากรถูกคอมไพล์, ไม่ได้ตรวจสอบขณะทำงาน) และปลอดภัยกว่า (คุณไม่สามารถเกิดการรวมฟีเจอร์แบบเสียค่าในรุ่นฟรีโดยบังเอิญจากข้อผิดพลาดในโค้ด)
ในโปรเจกต์หลายโมดูล แต่ละโมดูล (ไลบรารี) สามารถมี Build Variants ของตนเอง AGP จะซังโครไนซ์ variant โดยอัตโนมัติ: หากโมดูล app สร้าง paidRelease ไลบรารีที่ขึ้นอยู่กับก็จะถูกสร้างใน variant ที่ตรงกับ paidRelease เช่นกัน ปัญหาเกิดขึ้นเมื่อไลบรารีไม่มี product flavors แต่โมดูล app มี — จะสร้างไลบรารีหนึ่งครั้ง (release หรือ debug ขึ้นอยู่กับชนิด)
สำหรับโมดูลไลบรารี Build Variant โดยค่าเริ่มต้นจะตรงกับ Build Type ของโมดูล app เพราะไลบรารีไม่มี product flavors หากไลบรารีจำเป็นต้องปรับตัวให้เข้ากับ flavor ของโมดูล app ต้องประกาศ flavorDimensions และ productFlavors เดียวกันในไลบรารี AGP จับคู่ flavor โดยชื่อที่ตรงกันแท้ Gradle แนะนำให้ซังโครไนซ์ flavor ผ่านการกำหนดค่า build ในโปรเจกต์รากโดยใช้ subprojects หรือ Convention Plugins
เริ่มต้นแต่ AGP 8.1 ไลบรารีสามารถเผยแพร่ multiple variants — เผยแพร่ variant ทั้งหมดของไลบรารีไปยังคลัง maven พร้อมกัน วิธีนี้แก้ปัญหาเมื่อโมดูล app ใช้ flavor แบบเสียค่าแต่ไลบรารีเผยแพร่เฉพาะรุ่นฟรี Multiple variants publishing (MVP) ช่วยให้โปรเจกต์ที่ขึ้นอยู่เลือก variant ที่ต้องการโดยอัตโนมัติ เพื่อเปิดใช้ MVP ให้เพิ่ม publishing { multipleVariants { ... } } ใน build.gradle ของไลบรารี
บางครั้งจำเป็นต้องเปิดใช้งาน Build Variants บางส่วน — ตัวอย่างเช่น, หากการรวมกัน mockRelease ไม่มีความหมาย (เซิิร์ฟเวอร์จำลองไม่ควรไปที่ระบบ production) Gradle มี variantFilter — บล็อก DSL ที่คุณสามารถตรวจสอบคุณสมบัติของแต่ละ variant และเปิดใช้งานผ่าน setIgnore(true) VariantFilter ถูกใช้ในขั้นตอนการกำหนดค่า ก่อนการสร้างงาน ดังนั้น variant ที่ถูกเปิดใช้งานจะไม่สร้างงาน assemble และ install
การกรองก็มีประโยชน์ในการเร่ง build หากโปรเจกต์มี 8 variant แต่นักพัฒนาทำงานเฉพาะหนึ่ง variant ส่วนที่เหลือ 7 variant ยังคงต้องผ่านการกำหนดค่า เมื่อใช้ variantFilter variant ที่ถูกเปิดใช้งานจะไม่สร้างงาน ซึ่งช่วยลดเวลาการกำหนดค่าได้ 30-50% สำหรับโปรเจกต์ที่มี 6+ flavor dimensions ใน CI/CD คุณสามารถกรอง variant แบบไดนามิกผ่านพารามิเตอร์บรรทัดคำสั่ง -PbuildOnly=paidRelease
android {
variantFilter { variant ->
// ปิดใช้งาน mock สำหรับ release และ demo สำหรับ 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)
}
}
}
// การกรองแบบไดนามิกผ่านพารามิเตอร์
if (project.hasProperty("buildOnly")) {
def target = project.property("buildOnly")
android.variantFilter { variant ->
variant.setIgnore(variant.name != target)
}
}
คำถามที่พบบ่อย
ไม่มีขๅอจำกัด แต่ Gradle สร้าง ผลคูณแบบคาร์เทเซียน ของ flavor และชนิดทั้งหมด หากคุณมี 3 มิติ แต่ละมิติมี 3 flavor และ 3 build types คุณจะได้ 27 variant การมี variant มากเกินไปจะทำให้การกำหนดค่าช้าลง แนะนำไม่ให้มีเกิน 10–12 variant ในหนึ่งโมดูล
flavorDimensions จัดกลุ่ม Product Flavors เป็นแกนอิสระ ตัวอย่างเช่น, มิติ “tier” (free, paid) และมิติ “region” (us, eu) หากไม่มีมิติ flavor ทั้งหมดจะอยู่ในแกนเดียวกัน และ Gradle จะเลือกเฉพาะหนึ่ง flavor จากทั้งหมด (คุณไม่สามารถมี free+us และ paid+eu เป็น variant แยกต่างกัน)
ในบล็อก productFlavor หรือ buildType ระบุ applicationId ตัวอย่างเช่น, สำหรับรุ่นฟรี: free { applicationId “com.example.app.free” } ในแมนิเฟสต์ ใช้ ${applicationId} — Gradle จะแทนที่ค่าโดยอัตโนมัติ วิธีนี้ทำให้สามารถติดตั้งทั้งสอง variant บนเครื่องเดียวกัน
ใน iOS สิ่งที่เทียบเท่ากับ Build Variants คือการรวมกันของ Scheme + Configuration Xcode Schemes ถูกกำหนดค่าผ่านค่ากำหนด Debug/Release ที่มีพารามิเตอร์แตกต่างกัน สำหรับหลายเวอร์ชัน (free/paid) ใช้ Build Configurations และ Preprocessor Macros ใน Android แนวคิดนี้ถูกประเบ็นรูปแบบมากกว่าและสร้างไว้ใน Gradle
ใช่ แต่ละ variant สามารถมีขนาด APK แตกต่างกันได้ build แบบ debug รวมข้อมูล debug, SDK และ ทรัพยากรที่ไม่รองรับ build แบบ release ที่มี minification และ resource shrinking จะสร้างขนาดที่น้อยที่สุด Product Flavor ก็มีผลต่อขนาด: รุ่นฟรีที่ไม่มีไลบรารีแบบเสียค่าจะมีขนาดเล็กกว่ารุ่นแบบเสียค่าเท่ากับขนาดของไลบรารีเหล่านั้น
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม