ปลั๊กอิน Android Gradle (AGP) เป็นระบบบิลด์อย่างเป็นทางการสำหรับแอปพลิเคชัน Android ซึ่งรวมถึงการรองรับการคอมไพล์แหล่งที่มาประเภทต่างๆ และลิงก์เข้าด้วยกันเป็นแอปพลิเคชันที่คุณเรียกใช้ในอุปกรณ์ Android จริงหรือโปรแกรมจำลองได้
AGP มีจุดส่วนขยายสำหรับปลั๊กอินเพื่อควบคุมอินพุตของบิลด์และขยายฟังก์ชันการทำงานผ่านขั้นตอนใหม่ๆ ที่สามารถผสานรวมกับงานบิลด์มาตรฐานได้ AGP เวอร์ชันก่อนหน้าไม่มี API อย่างเป็นทางการที่แยกจากการใช้งานภายในอย่างชัดเจน ตั้งแต่เวอร์ชัน 7.0 เป็นต้นไป AGP มีชุด API ที่เป็นทางการและเสถียรซึ่งคุณสามารถใช้ได้
วงจร API ของ AGP
AGP เป็นไปตามวงจรฟีเจอร์ของ Gradle เพื่อกำหนดสถานะของ API ดังนี้
- ภายใน: ไม่ได้มีไว้สำหรับการใช้งานแบบสาธารณะ
- บ่มเพาะ: พร้อมให้สาธารณะใช้งานแต่ยังไม่ใช่เวอร์ชันสุดท้าย ซึ่งหมายความว่าอาจไม่เข้ากันได้แบบย้อนหลังในเวอร์ชันสุดท้าย
- สาธารณะ: พร้อมใช้งานแบบสาธารณะและมีความเสถียร
- เลิกใช้งานแล้ว: ไม่รองรับอีกต่อไป และแทนที่ด้วย API ใหม่
นโยบายการเลิกใช้งาน
AGP มีการพัฒนาอย่างต่อเนื่องด้วยการเลิกใช้งาน API เก่าและแทนที่ด้วย API ใหม่ที่เสถียรและ Domain Specific Language (DSL) ใหม่ การพัฒนาครั้งนี้จะครอบคลุม AGP หลายรุ่น และคุณสามารถดูข้อมูลเพิ่มเติมได้ที่ไทม์ไลน์การย้ายข้อมูล AGP API/DSL
เมื่อเลิกใช้งาน AGP API สำหรับการย้ายข้อมูลนี้หรืออื่นๆ API ดังกล่าวจะยังคงใช้งานได้ในเวอร์ชันหลักปัจจุบัน แต่จะสร้างคำเตือน ระบบจะนำ API ที่เลิกใช้งานแล้วออกจาก AGP โดยสมบูรณ์ในเวอร์ชันหลักถัดไป ตัวอย่างเช่น หาก API เลิกใช้งานใน AGP 7.0 แล้ว API ดังกล่าวจะยังคงใช้งานได้ในเวอร์ชันนั้นและสร้างคำเตือน API ดังกล่าวจะใช้ไม่ได้อีกต่อไปใน AGP 8.0
หากต้องการดูตัวอย่าง API ใหม่ที่ใช้ในการปรับแต่งบิลด์ทั่วไป โปรดดูสูตรปลั๊กอิน Android Gradle โดยจะแสดงตัวอย่างการปรับแต่งบิลด์ที่พบบ่อย นอกจากนี้ คุณยังดูรายละเอียดเพิ่มเติมเกี่ยวกับ API ใหม่ได้ในเอกสารอ้างอิง
คอมไพล์กับอาร์ติแฟกต์ AGP API
หากต้องการเขียนปลั๊กอิน Gradle ที่กำหนดเองหรือตรรกะการบิลด์ (เช่น ในไดเรกทอรี buildSrc, โมดูลปลั๊กอินแบบธรรมดา หรือโปรเจ็กต์ปลั๊กอินแบบสแตนด์อโลน) ที่ขยาย AGP ให้คอมไพล์กับอาร์ติแฟกต์ gradle-api ของ AGP แทนอาร์ติแฟกต์การติดตั้งใช้งานปลั๊กอินแบบเต็ม
เหตุผลที่ต้องคอมไพล์กับอาร์ติแฟกต์ gradle-api
AGP เผยแพร่อาร์ติแฟกต์หลัก 2 รายการ ได้แก่
com.android.tools.build:gradle-api: มี AGP API อย่างเป็นทางการ ซึ่งรวมถึง API ที่เสถียร (เช่นAndroidComponentsExtension,Variantและอินเทอร์เฟซ DSL สาธารณะ), API ที่อยู่ระหว่างการบ่มเพาะ (ทำเครื่องหมาย@Incubating) และ API ที่เลิกใช้งานแล้ว (ทำเครื่องหมาย@Deprecated)com.android.tools.build:gradle: มีการติดตั้งใช้งานรันไทม์ AGP แบบเต็ม ซึ่งรวมถึงคลาสภายใน งานบิลด์ และการผสานรวมคอมไพเลอร์
การคอมไพล์กับอาร์ติแฟกต์ gradle-api มีประโยชน์หลักๆ หลายประการ ดังนี้
- หลีกเลี่ยง API ภายใน: วิธีนี้ช่วยให้มั่นใจได้ว่าโค้ดปลั๊กอินจะเข้าถึงเฉพาะ API อย่างเป็นทางการเท่านั้น ซึ่งจะช่วยป้องกันการพึ่งพาคลาสการติดตั้งใช้งานภายใน (
com.android.build.gradle.internal.*) โดยไม่ตั้งใจ ซึ่งอาจมีการเปลี่ยนแปลงหรือถูกนำออกใน AGP เวอร์ชันต่างๆ โดยไม่มีคำเตือน - บิลด์เร็วขึ้นและใช้พื้นที่น้อยลง: อาร์ติแฟกต์
gradle-apiมีแผนผังทรัพยากร Dependency ที่เล็กกว่าอาร์ติแฟกต์gradleแบบเต็มมาก ซึ่งจะช่วยลดขนาดการดาวน์โหลด หลีกเลี่ยงการปนเปื้อนของ Classpath และเร่งการคอมไพล์ตรรกะของบิลด์ - การแยกส่วนรันไทม์: ปลั๊กอินที่กำหนดเองต้องใช้คำจำกัดความ API ในระหว่างการคอมไพล์เท่านั้น ขณะรันไทม์ การติดตั้งใช้งาน AGP แบบเต็มจะมาจากบิลด์ Android เมื่อใช้แอปพลิเคชัน Android หรือปลั๊กอินไลบรารี
เพิ่มทรัพยากร Dependency
เพิ่มอาร์ติแฟกต์ gradle-api เป็นทรัพยากร Dependency ในไฟล์บิลด์ของโปรเจ็กต์ปลั๊กอินหรือไดเรกทอรี buildSrc อาร์ติแฟกต์ gradle-api เผยแพร่อยู่ในที่เก็บ Maven ของ Google หากต้องการแก้ไขอาร์ติแฟกต์ ให้ตรวจสอบว่าบิลด์มีที่เก็บ google() เช่น กำหนดค่าในบล็อก dependencyResolutionManagement.repositories ของไฟล์ settings.gradle หรือ settings.gradle.kts หรือในบล็อก repositories ของไฟล์บิลด์ของปลั๊กอินหรือไฟล์บิลด์ของไดเรกทอรี buildSrc
ด้วยแคตตาล็อกเวอร์ชัน
หากโปรเจ็กต์ใช้แคตตาล็อกเวอร์ชัน ให้กำหนดทรัพยากร Dependency gradle-api ในไฟล์ gradle/libs.versions.toml ดังนี้
[versions]
androidGradlePlugin = "9.4.0"
[libraries]
android-gradle-plugin-api = { group = "com.android.tools.build", name = "gradle-api", version.ref = "androidGradlePlugin" }
ชื่อแทนแคตตาล็อก android-gradle-plugin-api จะสร้างตัวช่วยเข้าถึงที่ปลอดภัยในการกำหนดประเภท libs.android.gradle.plugin.api
จากนั้นเพิ่ม libs.android.gradle.plugin.api ลงในบล็อก dependencies ของไฟล์บิลด์ของปลั๊กอิน
Kotlin
dependencies { compileOnly(libs.android.gradle.plugin.api) }
ดึงดูด
dependencies { compileOnly libs.android.gradle.plugin.api }
ไม่มีแคตตาล็อกเวอร์ชัน
หากโปรเจ็กต์ไม่ได้ใช้แคตตาล็อกเวอร์ชัน ให้ประกาศทรัพยากร Dependency โดยตรงในไฟล์บิลด์ของปลั๊กอิน ดังนี้
Kotlin
dependencies { compileOnly("com.android.tools.build:gradle-api:9.4.0") }
ดึงดูด
dependencies { compileOnly 'com.android.tools.build:gradle-api:9.4.0' }
ใช้ตัวเลือกการกำหนดค่า compileOnly เป็นค่าเริ่มต้นสำหรับปลั๊กอินแบบสแตนด์อโลนที่มีไว้สำหรับการเผยแพร่ โปรเจ็กต์ที่ใช้จะใช้ AGP และระบุคลาสรันไทม์ใน classpath ของ buildscript การใช้การกำหนดค่า compileOnly จะป้องกันไม่ให้ปลั๊กอินที่เผยแพร่รั่วไหลของทรัพยากร Dependency ของรันไทม์ใน AGP เวอร์ชันที่เฉพาะเจาะจง และหลีกเลี่ยงความขัดแย้งของเวอร์ชันสำหรับผู้ใช้ หากคุณพัฒนาตรรกะการบิลด์ภายใน เช่น ปลั๊กอินในไดเรกทอรี buildSrc หรือปลั๊กอินแบบแผนในการบิลด์แบบผสม ให้ใช้ตัวเลือกการกำหนดค่า implementation แทน ปลั๊กอินเหล่านี้จะทำงานภายในบิลด์และต้องใช้ AGP ในคลาสพาธรันไทม์
ข้อมูลเบื้องต้นเกี่ยวกับบิลด์ Gradle
คู่มือนี้ไม่ได้ครอบคลุมระบบบิลด์ Gradle ทั้งหมด อย่างไรก็ตาม เอกสารนี้ครอบคลุมชุดแนวคิดที่จำเป็นขั้นต่ำเพื่อช่วยให้คุณผสานรวมกับ API ของเรา และลิงก์ไปยังเอกสารประกอบหลักของ Gradle เพื่ออ่านเพิ่มเติม
เราถือว่าคุณมีความรู้พื้นฐานเกี่ยวกับวิธีการทำงานของ Gradle รวมถึงวิธีกำหนดค่าโปรเจ็กต์ แก้ไขไฟล์บิลด์ ใช้ปลั๊กอิน และเรียกใช้งาน หากต้องการดูข้อมูลพื้นฐานของ Gradle เกี่ยวกับ AGP เราขอแนะนำให้อ่านกำหนดค่าบิลด์ ดูข้อมูลเกี่ยวกับเฟรมเวิร์กทั่วไปสำหรับการปรับแต่งปลั๊กอิน Gradle ได้ที่การพัฒนาปลั๊กอิน Gradle ที่กำหนดเอง
อภิธานศัพท์ประเภท Lazy ของ Gradle
Gradle มีประเภทต่างๆ มากมายที่ทำงานแบบ "Lazy" หรือช่วยเลื่อนการคำนวณหรือการสร้าง Task ที่ใช้ทรัพยากรมากไปยังระยะหลังๆ ของการบิลด์ ประเภทเหล่านี้เป็นหัวใจสำคัญของ API ของ Gradle และ AGP หลายรายการ รายการต่อไปนี้ประกอบด้วยประเภท Gradle หลักที่เกี่ยวข้องกับการดำเนินการแบบ Lazy และเมธอดหลักของประเภทเหล่านั้น
Provider<T>- ระบุค่าของประเภท
T(โดยที่ "T" หมายถึงประเภทใดก็ได้) ซึ่งสามารถอ่านได้ในระหว่างระยะการดำเนินการโดยใช้get()หรือเปลี่ยนเป็นProvider<S>ใหม่ (โดยที่ "S" หมายถึงประเภทอื่นๆ) โดยใช้เมธอดmap(),flatMap()และzip()โปรดทราบว่าไม่ควรเรียกใช้get()ในระหว่างขั้นตอนการกำหนดค่าmap(): ยอมรับ lambda และสร้างProviderประเภทS,Provider<S>อาร์กิวเมนต์ lambda สำหรับmap()จะรับค่าTและสร้างค่าSระบบจะไม่เรียกใช้ Lambda ทันที แต่จะเลื่อนการเรียกใช้ไปจนกว่าจะมีการเรียกใช้get()ในProvider<S>ที่เป็นผลลัพธ์ ซึ่งทำให้ทั้งเชนเป็นแบบ LazyflatMap(): ยังยอมรับ Lambda และสร้างProvider<S>ด้วย แต่ Lambda จะใช้ค่าTและสร้างProvider<S>(แทนที่จะสร้างค่าSโดยตรง) ใช้ flatMap() เมื่อไม่สามารถระบุ S ได้ในเวลาที่กำหนดค่า และคุณจะได้รับเฉพาะProvider<S>เท่านั้น ในทางปฏิบัติ หากคุณใช้map()และได้ผลลัพธ์ประเภทProvider<Provider<S>>ก็อาจหมายความว่าคุณควรใช้flatMap()แทนzip(): ช่วยให้คุณรวมอินสแตนซ์Provider2 รายการเพื่อสร้างProviderใหม่ โดยมีค่าที่คำนวณโดยใช้ฟังก์ชันที่รวมค่าจากอินสแตนซ์Providersอินพุต 2 รายการ
Property<T>- ใช้
Provider<T>จึงให้ค่าประเภทTด้วย คุณยังตั้งค่าสำหรับProperty<T>ได้ด้วย ซึ่งต่างจากProvider<T>ที่เป็นแบบอ่านอย่างเดียว ซึ่งทำได้ 2 วิธีดังนี้- ตั้งค่าประเภท
Tโดยตรงเมื่อพร้อมใช้งาน โดยไม่ต้องคำนวณแบบเลื่อนเวลา - ตั้งค่า
Provider<T>อื่นเป็นแหล่งที่มาของค่าProperty<T>ในกรณีนี้ ค่าTจะเกิดขึ้นจริงเมื่อมีการเรียกใช้Property.get()เท่านั้น
- ตั้งค่าประเภท
TaskProvider- การใช้งาน
Provider<Task>หากต้องการสร้างTaskProviderให้ใช้tasks.register()ไม่ใช่tasks.create()เพื่อให้มั่นใจว่าระบบจะสร้างอินสแตนซ์ของงานแบบ Lazy-instantiation เมื่อจำเป็นเท่านั้น คุณสามารถใช้flatMap()เพื่อเข้าถึงเอาต์พุตของTaskก่อนที่จะสร้างTaskซึ่งจะเป็นประโยชน์หากคุณต้องการใช้เอาต์พุตเป็นอินพุตสำหรับอินสแตนซ์Taskอื่นๆ
ผู้ให้บริการและวิธีการเปลี่ยนรูปแบบเป็นสิ่งสำคัญสำหรับการตั้งค่าอินพุตและเอาต์พุตของงานในลักษณะ Lazy กล่าวคือ ไม่จำเป็นต้องสร้างงานทั้งหมดล่วงหน้าและแก้ไขค่า
นอกจากนี้ ผู้ให้บริการยังมีข้อมูลการขึ้นต่อกันของงานด้วย เมื่อคุณสร้าง Provider โดย
เปลี่ยนรูปแบบเอาต์พุต Task เอาต์พุต Task นั้นจะกลายเป็นทรัพยากร Dependency โดยนัยของ
Provider และจะสร้างและเรียกใช้เมื่อมีการแก้ไขค่าของ Provider
เช่น เมื่อ Task อื่นต้องการ
ต่อไปนี้เป็นตัวอย่างการลงทะเบียนงาน 2 งาน ได้แก่ GitVersionTask และ ManifestProducerTask โดยเลื่อนการสร้างอินสแตนซ์ Task ออกไปจนกว่าจะจำเป็นต้องใช้จริงๆ ค่าอินพุต ManifestProducerTask ตั้งค่าเป็น Provider ที่ได้จากเอาต์พุตของ GitVersionTask ดังนั้น ManifestProducerTask จึงขึ้นอยู่กับ GitVersionTask โดยนัย
// Register a task lazily to get its TaskProvider.
val gitVersionProvider: TaskProvider =
project.tasks.register("gitVersionProvider", GitVersionTask::class.java) {
it.gitVersionOutputFile.set(
File(project.buildDir, "intermediates/gitVersionProvider/output")
)
}
...
/**
* Register another task in the configuration block (also executed lazily,
* only if the task is required).
*/
val manifestProducer =
project.tasks.register(variant.name + "ManifestProducer", ManifestProducerTask::class.java) {
/**
* Connect this task's input (gitInfoFile) to the output of
* gitVersionProvider.
*/
it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
}
งาน 2 อย่างนี้จะดำเนินการก็ต่อเมื่อมีการขออย่างชัดแจ้งเท่านั้น ซึ่งอาจเกิดขึ้นได้เมื่อมีการเรียกใช้ Gradle เช่น หากคุณเรียกใช้ ./gradlew
debugManifestProducer หรือหากเอาต์พุตของ ManifestProducerTask เชื่อมต่อกับงานอื่นและจำเป็นต้องใช้ค่าของเอาต์พุตนั้น
แม้ว่าคุณจะเขียนงานที่กำหนดเองซึ่งใช้ข้อมูลเข้าและ/หรือสร้างเอาต์พุต แต่ AGP จะไม่เสนอการเข้าถึงแบบสาธารณะสำหรับงานของตนเองโดยตรง โดยเป็นรายละเอียดการติดตั้งใช้งานที่อาจมีการเปลี่ยนแปลงในแต่ละเวอร์ชัน แต่ AGP จะมี Variant API และสิทธิ์เข้าถึงเอาต์พุตของงานหรืออาร์ติแฟกต์ของบิลด์ที่คุณอ่านและเปลี่ยนรูปแบบได้ ดูข้อมูลเพิ่มเติมได้ที่Variant API, อาร์ติแฟกต์ และงานในเอกสารนี้
ระยะการบิลด์ Gradle
การสร้างโปรเจ็กต์เป็นกระบวนการที่ซับซ้อนและต้องใช้ทรัพยากรมากโดยเนื้อแท้ และมีฟีเจอร์ต่างๆ เช่น การหลีกเลี่ยงการกำหนดค่างาน การตรวจสอบข้อมูลล่าสุด และฟีเจอร์การแคชการกำหนดค่าที่ช่วยลดเวลาที่ใช้ในการคำนวณที่ทำซ้ำได้หรือไม่จำเป็น
หากต้องการใช้การเพิ่มประสิทธิภาพเหล่านี้ สคริปต์และปลั๊กอิน Gradle ต้องปฏิบัติตามกฎที่เข้มงวดในระหว่างระยะการสร้าง Gradle ที่แตกต่างกันแต่ละระยะ ได้แก่ การเริ่มต้น การกำหนดค่า และการดำเนินการ ในคู่มือนี้ เราจะมุ่งเน้นที่ ระยะการกำหนดค่าและการดำเนินการ ดูข้อมูลเพิ่มเติมเกี่ยวกับทุกระยะได้ในคู่มือวงจรการสร้าง Gradle
ระยะการกำหนดค่า
ในระหว่างขั้นตอนการกำหนดค่า ระบบจะประเมินสคริปต์บิลด์สำหรับโปรเจ็กต์ทั้งหมดที่เป็นส่วนหนึ่งของบิลด์ ใช้ปลั๊กอิน และแก้ไขทรัพยากร Dependency ของบิลด์ ควรใช้ระยะนี้เพื่อกำหนดค่าบิลด์โดยใช้ออบเจ็กต์ DSL และเพื่อลงทะเบียนงานและอินพุตของงานแบบ Lazy
เนื่องจากระยะการกำหนดค่าจะทำงานเสมอ ไม่ว่าจะมีการขอให้ทำงานใดก็ตาม จึงเป็นสิ่งสำคัญอย่างยิ่งที่จะต้องทำให้ระยะนี้มีขนาดเล็กและจำกัดการคำนวณไม่ให้ขึ้นอยู่กับอินพุตอื่นนอกเหนือจากสคริปต์บิลด์เอง
กล่าวคือ คุณไม่ควรเรียกใช้โปรแกรมภายนอกหรืออ่านจากเครือข่าย หรือทำการคำนวณที่ใช้เวลานานซึ่งสามารถเลื่อนไปไว้ในระยะการดำเนินการเป็นอินสแตนซ์ Task ที่เหมาะสมได้
ระยะการดำเนินการ
ในระยะการดำเนินการ ระบบจะดำเนินการกับงานที่ขอและงานที่เกี่ยวข้อง โดยเฉพาะอย่างยิ่ง เมธอดคลาส Task ที่ทำเครื่องหมายด้วย @TaskAction จะทำงาน ในระหว่างการดำเนินการงาน คุณจะได้รับอนุญาตให้อ่านจากอินพุต (เช่น ไฟล์) และแก้ไขผู้ให้บริการแบบ Lazy โดยเรียกใช้ Provider<T>.get() การแก้ไขผู้ให้บริการแบบ Lazy ด้วยวิธีนี้จะเริ่มลำดับการเรียก map() หรือ flatMap() ที่เป็นไปตามข้อมูลการขึ้นต่อกันของงานที่อยู่ในผู้ให้บริการ ระบบจะเรียกใช้ Tasks แบบ Lazy เพื่อสร้างค่าที่จำเป็น
Variant API, อาร์ติแฟกต์ และ Tasks
Variant API เป็นกลไกส่วนขยายในปลั๊กอิน Android Gradle ที่ช่วยให้คุณจัดการตัวเลือกต่างๆ ซึ่งโดยปกติจะตั้งค่าโดยใช้ DSL ในไฟล์การกำหนดค่าบิลด์ ซึ่งส่งผลต่อบิลด์ Android นอกจากนี้ Variant API ยังให้คุณเข้าถึงอาร์ติแฟกต์ระดับกลางและขั้นสุดท้ายที่สร้างขึ้นโดยบิลด์ เช่น ไฟล์คลาส, ไฟล์ Manifest ที่ผสานแล้ว หรือไฟล์ APK/AAB
ขั้นตอนการสร้าง Android และจุดขยาย
เมื่อโต้ตอบกับ AGP ให้ใช้จุดส่วนขยายที่สร้างขึ้นเป็นพิเศษแทนการลงทะเบียน Lifecycle Callback ของ Gradle ทั่วไป (เช่น afterEvaluate()) หรือการตั้งค่าทรัพยากร Dependency ของ Task ที่ชัดเจน งานที่สร้างโดย AGP จะถือเป็นรายละเอียดการติดตั้งใช้งานและไม่แสดงเป็น API สาธารณะ คุณต้องหลีกเลี่ยงการพยายามรับอินสแตนซ์ของออบเจ็กต์ Task หรือเดาชื่อ Task และเพิ่ม Callback หรือทรัพยากร Dependency ลงในออบเจ็กต์ Task เหล่านั้นโดยตรง
AGP จะทำตามขั้นตอนต่อไปนี้เพื่อสร้างและเรียกใช้อินสแตนซ์ Task ซึ่งจะสร้างอาร์ติแฟกต์ของบิลด์ ขั้นตอนหลักที่เกี่ยวข้องกับการสร้างออบเจ็กต์ Variant จะตามด้วย Callback ที่ช่วยให้คุณทำการเปลี่ยนแปลงออบเจ็กต์บางอย่างที่สร้างขึ้นเป็นส่วนหนึ่งของบิลด์ได้ โปรดทราบว่า Callback ทั้งหมดจะเกิดขึ้นในขั้นตอนการกำหนดค่า (อธิบายไว้ในหน้านี้) และต้องทำงานอย่างรวดเร็ว โดยเลื่อนงานที่ซับซ้อนไปยังอินสแตนซ์ Task ที่เหมาะสมในขั้นตอนการดำเนินการแทน
- การแยกวิเคราะห์ DSL: ขั้นตอนนี้คือการประเมินสคริปต์บิลด์ และสร้างและตั้งค่าพร็อพเพอร์ตี้ต่างๆ ของออบเจ็กต์ Android DSL จากบล็อก
androidนอกจากนี้ ระบบจะลงทะเบียน Callback ของ Variant API ที่อธิบายไว้ในส่วนต่อไปนี้ในระยะนี้ด้วย finalizeDsl(): Callback ที่ช่วยให้คุณเปลี่ยนออบเจ็กต์ DSL ก่อนที่จะล็อกเพื่อสร้างคอมโพเนนต์ (ตัวแปร) ระบบจะสร้างออบเจ็กต์VariantBuilderตามข้อมูลที่อยู่ในออบเจ็กต์ DSLการล็อก DSL: ตอนนี้ DSL ล็อกอยู่และไม่สามารถทำการเปลี่ยนแปลงได้อีกต่อไป
beforeVariants(): Callback นี้อาจส่งผลต่อคอมโพเนนต์ที่สร้างขึ้นและพร็อพเพอร์ตี้บางอย่างของคอมโพเนนต์ผ่านVariantBuilderแต่ยังคงอนุญาตให้แก้ไขขั้นตอนการสร้างและอาร์ติแฟกต์ที่สร้างขึ้นได้การสร้างตัวแปร: รายการคอมโพเนนต์และอาร์ติแฟกต์ที่จะสร้างได้รับการสรุปแล้วและไม่สามารถเปลี่ยนแปลงได้
onVariants(): ใน Callback นี้ คุณจะเข้าถึงออบเจ็กต์Variantที่สร้างขึ้นได้ และสามารถตั้งค่าหรือผู้ให้บริการสำหรับค่าPropertyที่มีอยู่ เพื่อให้คำนวณแบบ Lazy ได้การล็อกตัวแปร: ระบบจะล็อกออบเจ็กต์ตัวแปรและไม่สามารถทำการเปลี่ยนแปลงได้อีกต่อไป
งานที่สร้างขึ้น: ระบบจะใช้ออบเจ็กต์
Variantและค่าPropertyของออบเจ็กต์เพื่อสร้างอินสแตนซ์Taskที่จำเป็นต่อการสร้าง
AGP เปิดตัว AndroidComponentsExtension ที่ช่วยให้คุณลงทะเบียน Callback สำหรับ finalizeDsl(), beforeVariants() และ onVariants() ได้
ส่วนขยายนี้พร้อมใช้งานในสคริปต์บิลด์ผ่านบล็อก androidComponents ดังนี้
// This is used only for configuring the Android build through DSL.
android { ... }
// The androidComponents block is separate from the DSL.
androidComponents {
finalizeDsl { extension ->
...
}
}
อย่างไรก็ตาม เราขอแนะนำให้เก็บสคริปต์บิลด์ไว้สำหรับการกำหนดค่าแบบประกาศเท่านั้นโดยใช้ DSL ของบล็อก android และย้ายตรรกะเชิงคำสั่งที่กำหนดเองไปยังไดเรกทอรี buildSrc หรือปลั๊กอินภายนอก นอกจากนี้ คุณยังดูตัวอย่าง buildSrc ในที่เก็บ GitHub ของสูตร Gradle เพื่อดูวิธีสร้างปลั๊กอินในโปรเจ็กต์ได้ด้วย ตรวจสอบว่าปลั๊กอินคอมไพล์กับอาร์ติแฟกต์ gradle-api ในไฟล์บิลด์ ต่อไปนี้คือตัวอย่างการลงทะเบียน Callback จากโค้ดปลั๊กอิน
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
...
}
}
}
มาดูรายละเอียดเกี่ยวกับ Callback ที่พร้อมใช้งานและประเภทของ Use Case ที่ปลั๊กอินของคุณรองรับในแต่ละ Callback กัน
finalizeDsl(callback: (DslExtensionT) -> Unit)
ใน Callback นี้ คุณจะเข้าถึงและแก้ไขออบเจ็กต์ DSL ที่สร้างขึ้นโดยการแยกวิเคราะห์ข้อมูลจากบล็อก android ในไฟล์บิลด์ได้
ออบเจ็กต์ DSL เหล่านี้จะใช้เพื่อเริ่มต้นและกำหนดค่าตัวแปรในระยะหลังๆ ของบิลด์ ตัวอย่างเช่น คุณสามารถสร้างการกำหนดค่าใหม่หรือลบล้างพร็อพเพอร์ตี้แบบเป็นโปรแกรมได้ แต่โปรดทราบว่าค่าทั้งหมดต้องได้รับการแก้ไขในเวลาที่กำหนดค่า ดังนั้นจึงต้องไม่พึ่งพาอินพุตภายนอกใดๆ
หลังจากที่ Callback นี้ดำเนินการเสร็จสิ้นแล้ว ออบเจ็กต์ DSL จะไม่มีประโยชน์อีกต่อไป และคุณไม่ควรเก็บการอ้างอิงไปยังออบเจ็กต์เหล่านั้นหรือแก้ไขค่าของออบเจ็กต์เหล่านั้นอีก
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
extension.buildTypes.create("extra").let {
it.isJniDebuggable = true
}
}
}
}
beforeVariants()
ในขั้นตอนนี้ของการสร้าง คุณจะเข้าถึงออบเจ็กต์ VariantBuilder ซึ่งจะกำหนดตัวแปรที่จะสร้างและพร็อพเพอร์ตี้ของตัวแปรเหล่านั้น เช่น คุณสามารถปิดใช้ตัวแปรบางรายการ การทดสอบของตัวแปรเหล่านั้น หรือเปลี่ยนค่าของพร็อพเพอร์ตี้ (เช่น minSdk) โดยใช้โปรแกรมสำหรับตัวแปรที่เลือกเท่านั้น เช่นเดียวกับ finalizeDsl() ค่าทั้งหมดที่คุณระบุต้องได้รับการแก้ไข ณ เวลาที่กำหนดค่าและไม่ขึ้นอยู่กับอินพุตภายนอก ต้องไม่มีการแก้ไขออบเจ็กต์ VariantBuilder เมื่อการเรียกกลับ beforeVariants() ดำเนินการเสร็จสิ้นแล้ว
androidComponents {
beforeVariants { variantBuilder ->
variantBuilder.minSdk = 23
}
}
Callback beforeVariants() จะใช้ VariantSelector หรือไม่ก็ได้ ซึ่งคุณสามารถรับได้ผ่านเมธอด selector() ใน androidComponentsExtension คุณสามารถใช้เพื่อกรองคอมโพเนนต์ที่เข้าร่วมในการเรียกใช้ Callback ตามชื่อ ประเภทบิลด์ หรือเวอร์ชันผลิตภัณฑ์
androidComponents {
beforeVariants(selector().withName("adfree")) { variantBuilder ->
variantBuilder.minSdk = 23
}
}
onVariants()
เมื่อมีการเรียกใช้ onVariants() ระบบจะตัดสินใจเลือกอาร์ติแฟกต์ทั้งหมดที่ AGP จะสร้างขึ้นแล้ว คุณจึงปิดใช้อาร์ติแฟกต์เหล่านั้นไม่ได้อีกต่อไป อย่างไรก็ตาม คุณสามารถแก้ไขค่าบางค่าที่ใช้สำหรับงานได้โดยตั้งค่าสำหรับแอตทริบิวต์ Property ในออบเจ็กต์ Variant เนื่องจากค่า Property จะได้รับการแก้ไขเมื่อมีการเรียกใช้ Task ของ AGP เท่านั้น คุณจึงเชื่อมต่อค่าเหล่านี้กับผู้ให้บริการจาก Task ที่กำหนดเองซึ่งจะทำการคำนวณที่จำเป็นได้อย่างปลอดภัย รวมถึงการอ่านจากอินพุตภายนอก เช่น ไฟล์หรือเครือข่าย
// onVariants also supports VariantSelectors:
onVariants(selector().withBuildType("release")) { variant ->
// Gather the output when we are in single mode (no multi-apk).
val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }
// Create version code generating task
val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
it.outputFile.set(project.layout.buildDirectory.file("${variant.name}/versionCode.txt"))
}
/**
* Wire version code from the task output.
* map() will create a lazy provider that:
* 1. Runs just before the consumer(s), ensuring that the producer
* (VersionCodeTask) has run and therefore the file is created.
* 2. Contains task dependency information so that the consumer(s) run after
* the producer.
*/
mainOutput.versionCode.set(versionCodeTask.map { it.outputFile.get().asFile.readText().toInt() })
}
เพิ่มแหล่งที่มาที่สร้างขึ้นลงในบิลด์
ปลั๊กอินของคุณสามารถสร้างแหล่งที่มาที่สร้างขึ้นได้ 2-3 ประเภท เช่น
- โค้ดของแอปพลิเคชันในไดเรกทอรี
java - แหล่งข้อมูล Android ในไดเรกทอรี
res - ทรัพยากร Java
ในไดเรกทอรี
resources - เนื้อหา Android ในไดเรกทอรี
assets
ดูรายการแหล่งที่มาทั้งหมดที่คุณเพิ่มได้ที่ Sources API
ข้อมูลโค้ดนี้แสดงวิธีเพิ่มโฟลเดอร์แหล่งที่มาที่กำหนดเองชื่อ ${variant.name} ลงในชุดซอร์ส Java โดยใช้ฟังก์ชัน addStaticSourceDirectory() จากนั้น Toolchain ของ Android จะประมวลผลโฟลเดอร์นี้
onVariants { variant ->
variant.sources.java?.let { java ->
java.addStaticSourceDirectory("custom/src/kotlin/${variant.name}")
}
}
ดูรายละเอียดเพิ่มเติมได้ที่สูตร addJavaSource
ข้อมูลโค้ดนี้แสดงวิธีเพิ่มไดเรกทอรีที่มีทรัพยากร Android ที่สร้างจากงานที่กำหนดเองลงในชุดซอร์ส res โดยแหล่งที่มาประเภทอื่นๆ ก็จะมีกระบวนการที่คล้ายคลึงกันนี้
onVariants(selector().withBuildType("release")) { variant ->
// Step 1. Register the task.
val resCreationTask =
project.tasks.register<ResCreatorTask>("create${variant.name}Res")
// Step 2. Register the task output to the variant-generated source directory.
variant.sources.res?.addGeneratedSourceDirectory(
resCreationTask,
ResCreatorTask::outputDirectory)
}
...
// Step 3. Define the task.
abstract class ResCreatorTask: DefaultTask() {
@get:OutputFiles
abstract val outputDirectory: DirectoryProperty
@TaskAction
fun taskAction() {
// Step 4. Generate your resources.
...
}
}
ดูรายละเอียดเพิ่มเติมได้ที่สูตร addCustomAsset
เข้าถึงและแก้ไขอาร์ติแฟกต์
นอกจากจะช่วยให้คุณแก้ไขพร็อพเพอร์ตี้ง่ายๆ ในออบเจ็กต์ Variant แล้ว AGP ยังมีกลไกส่วนขยายที่ช่วยให้คุณอ่านหรือเปลี่ยนรูปแบบอาร์ติแฟกต์ขั้นกลางและขั้นสุดท้ายที่สร้างขึ้นระหว่างการบิลด์ได้ด้วย ตัวอย่างเช่น คุณสามารถอ่านเนื้อหาไฟล์ AndroidManifest.xml ที่ผสานรวมขั้นสุดท้ายใน Task ที่กำหนดเองเพื่อวิเคราะห์ หรือจะแทนที่เนื้อหาทั้งหมดด้วยเนื้อหาของไฟล์ Manifest ที่สร้างโดย Task ที่กำหนดเองก็ได้
คุณดูรายการอาร์ติแฟกต์ที่รองรับในปัจจุบันได้ในเอกสารอ้างอิงสำหรับคลาส Artifact อาร์ติแฟกต์แต่ละประเภทมีคุณสมบัติบางอย่างที่ควรทราบ ดังนี้
Cardinality
จำนวนสมาชิกในเซ็ตของ Artifact แสดงถึงจำนวนอินสแตนซ์ FileSystemLocation หรือจำนวนไฟล์หรือไดเรกทอรีของประเภทอาร์ติแฟกต์ คุณสามารถดูข้อมูลเกี่ยวกับจำนวนสมาชิกในเซ็ตของอาร์ติแฟกต์ได้โดยการตรวจสอบคลาสหลักของอาร์ติแฟกต์นั้น อาร์ติแฟกต์ที่มี FileSystemLocation รายการเดียวจะเป็นคลาสย่อยของ Artifact.Single ส่วนอาร์ติแฟกต์ที่มีอินสแตนซ์ FileSystemLocation หลายรายการจะเป็นคลาสย่อยของ Artifact.Multiple
ประเภท FileSystemLocation
คุณสามารถตรวจสอบว่า Artifact แสดงไฟล์หรือไดเรกทอรีได้โดยดูที่ประเภท FileSystemLocation ที่กำหนดพารามิเตอร์ ซึ่งอาจเป็น RegularFile หรือ Directory ก็ได้
การดำเนินการที่รองรับ
คลาส Artifact ทุกคลาสสามารถใช้อินเทอร์เฟซต่อไปนี้เพื่อระบุการดำเนินการที่รองรับ
Transformable: อนุญาตให้ใช้Artifactเป็นอินพุตสำหรับTaskที่ทำการเปลี่ยนรูปแบบตามต้องการและแสดงผลArtifactเวอร์ชันใหม่Appendable: ใช้กับอาร์ติแฟกต์ที่เป็นคลาสย่อยของArtifact.Multipleเท่านั้น ซึ่งหมายความว่าสามารถเพิ่มArtifactได้ กล่าวคือTaskที่กำหนดเองสามารถสร้างอินสแตนซ์ใหม่ของประเภทArtifactนี้ซึ่งจะเพิ่มลงในรายการที่มีอยู่Replaceable: ใช้กับอาร์ติแฟกต์ที่เป็นคลาสย่อยของArtifact.Singleเท่านั้นArtifactที่เปลี่ยนแทนได้สามารถแทนที่ด้วยอินสแตนซ์ใหม่ทั้งหมด ซึ่งสร้างขึ้นเป็นเอาต์พุตของTask
นอกเหนือจากการดำเนินการแก้ไขอาร์ติแฟกต์ 3 รายการแล้ว อาร์ติแฟกต์ทุกรายการยังรองรับการดำเนินการ get() (หรือ getAll()) ซึ่งจะแสดงผล Provider พร้อมอาร์ติแฟกต์เวอร์ชันสุดท้าย (หลังจากการดำเนินการทั้งหมดเสร็จสิ้น)
ปลั๊กอินหลายรายการสามารถเพิ่มการดำเนินการกับอาร์ติแฟกต์จำนวนเท่าใดก็ได้ลงในไปป์ไลน์จาก Callback onVariants() และ AGP จะตรวจสอบว่ามีการเชื่อมโยงอย่างถูกต้องเพื่อให้งานทั้งหมดทำงานในเวลาที่เหมาะสม และอาร์ติแฟกต์ได้รับการสร้างและอัปเดตอย่างถูกต้อง ซึ่งหมายความว่าเมื่อการดำเนินการเปลี่ยนเอาต์พุตใดๆ โดยการเพิ่ม แทนที่ หรือเปลี่ยนรูปแบบ การดำเนินการถัดไปจะเห็นอาร์ติแฟกต์เวอร์ชันที่อัปเดตเป็นอินพุต และอื่นๆ
จุดแรกเข้าในการลงทะเบียนการดำเนินการคือคลาส Artifacts
ข้อมูลโค้ดต่อไปนี้แสดงวิธีเข้าถึงอินสแตนซ์ของ Artifacts จากพร็อพเพอร์ตี้ในออบเจ็กต์ Variant ใน Callback onVariants()
จากนั้นคุณสามารถส่ง TaskProvider ที่กำหนดเองเพื่อรับออบเจ็กต์ TaskBasedOperation (1) และใช้เพื่อเชื่อมต่ออินพุตและเอาต์พุตโดยใช้เมธอด wiredWith* (2) วิธีใดวิธีหนึ่ง
วิธีการที่แน่นอนที่คุณต้องเลือกจะขึ้นอยู่กับจำนวนสมาชิกในเซ็ตและประเภท FileSystemLocation ที่ใช้โดย Artifact ที่คุณต้องการเปลี่ยนรูปแบบ
และสุดท้าย คุณส่งประเภท Artifact ไปยังเมธอดที่แสดงถึงการดำเนินการที่เลือกในออบเจ็กต์ *OperationRequest ที่คุณได้รับเป็นการตอบแทน เช่น toAppendTo(), toTransform() หรือ toCreate() (3)
androidComponents.onVariants { variant ->
val manifestUpdater = // Custom task that will be used for the transform.
project.tasks.register(variant.name + "ManifestUpdater", ManifestTransformerTask::class.java) {
it.gitInfoFile.set(gitVersionProvider.flatMap(GitVersionTask::gitVersionOutputFile))
}
// (1) Register the TaskProvider w.
val variant.artifacts.use(manifestUpdater)
// (2) Connect the input and output files.
.wiredWithFiles(
ManifestTransformerTask::mergedManifest,
ManifestTransformerTask::updatedManifest)
// (3) Indicate the artifact and operation type.
.toTransform(SingleArtifact.MERGED_MANIFEST)
}
ในตัวอย่างนี้ MERGED_MANIFEST คือ SingleArtifact และเป็น RegularFile ด้วยเหตุนี้ เราจึงต้องใช้เมธอด wiredWithFiles ซึ่งยอมรับการอ้างอิง RegularFileProperty รายการเดียวสำหรับอินพุต และ RegularFileProperty รายการเดียวสำหรับเอาต์พุต มีเมธอด wiredWith* อื่นๆ ในคลาส TaskBasedOperation ที่จะใช้ได้กับการรวมจำนวนสมาชิกในเซ็ต Artifact และประเภท FileSystemLocation อื่นๆ
หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับการขยาย AGP เราขอแนะนำให้อ่านส่วนต่อไปนี้จากคู่มือระบบบิลด์ Gradle
- การพัฒนาปลั๊กอิน Gradle ที่กำหนดเอง
- การใช้ปลั๊กอิน Gradle
- การพัฒนาประเภทงาน Gradle ที่กำหนดเอง
- การกำหนดค่าแบบ Lazy
- การหลีกเลี่ยงการกำหนดค่างาน