ปลั๊กอิน Android Gradle (AGP) เป็นระบบบิลด์อย่างเป็นทางการสำหรับแอปพลิเคชัน Android แพลตฟอร์มนี้รองรับการคอมไพล์แหล่งที่มาประเภทต่างๆ และลิงก์เข้าด้วยกันเป็นแอปพลิเคชันที่คุณเรียกใช้บนอุปกรณ์ Android จริงหรือโปรแกรมจำลองได้
AGP มีจุดขยายสำหรับปลั๊กอินเพื่อควบคุมอินพุตการสร้างและขยายฟังก์ชันการทำงานผ่านขั้นตอนใหม่ที่ผสานรวมกับงานการสร้างมาตรฐานได้ AGP เวอร์ชันก่อนหน้าไม่มี API อย่างเป็นทางการที่แยกออกจากการใช้งานภายในอย่างชัดเจน ตั้งแต่เวอร์ชัน 7.0 AGP มีชุดAPI อย่างเป็นทางการที่เสถียรซึ่งคุณไว้วางใจได้
วงจรชีวิตของ AGP API
AGP เป็นไปตามวงจรของฟีเจอร์ Gradle เพื่อกำหนดสถานะของ API ดังนี้
- ภายใน: ไม่ได้มีไว้สำหรับการใช้งานสาธารณะ
- กำลังพัฒนา: พร้อมให้ใช้งานแบบสาธารณะแต่ยังไม่เป็นเวอร์ชันสุดท้าย ซึ่งหมายความว่าอาจใช้งานร่วมกับเวอร์ชันเก่าไม่ได้ในเวอร์ชันสุดท้าย
- สาธารณะ: พร้อมให้ใช้งานแบบสาธารณะและมีความเสถียร
- เลิกใช้งานแล้ว: ระบบไม่รองรับอีกต่อไปและมี API ใหม่เข้ามาแทนที่
นโยบายการเลิกใช้งาน
AGP มีการพัฒนาจากการเลิกใช้งาน API เก่าและการแทนที่ 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 ใหม่ที่ใช้ในการปรับแต่งบิลด์ทั่วไป โปรดดูสูตรสำหรับปลั๊กอิน Gradle ของ Android ซึ่งแสดงตัวอย่างการปรับแต่งบิลด์ที่พบบ่อย นอกจากนี้ คุณยังดูรายละเอียดเพิ่มเติมเกี่ยวกับ API ใหม่ได้ในเอกสารอ้างอิง
ข้อมูลเบื้องต้นเกี่ยวกับการสร้างของ Gradle
คำแนะนำนี้ไม่ได้ครอบคลุมระบบการสร้าง Gradle ทั้งหมด อย่างไรก็ตาม เอกสารประกอบนี้ครอบคลุมชุดแนวคิดที่จําเป็นขั้นต่ำเพื่อช่วยให้คุณผสานรวมกับ API ของเราได้ และลิงก์ไปยังเอกสารประกอบหลักของ Gradle เพื่ออ่านเพิ่มเติม
เราคาดเดาความรู้พื้นฐานเกี่ยวกับวิธีการทำงานของ Gradle ซึ่งรวมถึงวิธีกำหนดค่าโปรเจ็กต์ แก้ไขไฟล์บิลด์ ใช้ปลั๊กอิน และเรียกใช้งาน หากต้องการดูข้อมูลเบื้องต้นเกี่ยวกับ Gradle ที่เกี่ยวข้องกับ AGP เราขอแนะนําให้ดูกําหนดค่าการสร้าง ดูข้อมูลเกี่ยวกับเฟรมเวิร์กทั่วไปสำหรับการปรับแต่งปลั๊กอิน Gradle ได้ที่หัวข้อการพัฒนาปลั๊กอิน Gradle ที่กําหนดเอง
อภิธานศัพท์เกี่ยวกับประเภทแบบ Lazy ของ Gradle
Gradle มีประเภทต่างๆ ที่ทำงานแบบ "ขี้เกียจ" หรือช่วยเลื่อนการคำนวณที่หนักหน่วงหรือการสร้าง Task
ไปไว้ในช่วงหลังของการสร้าง ประเภทเหล่านี้เป็นหัวใจสำคัญของ Gradle และ AGP API หลายรายการ รายการต่อไปนี้มีประเภท 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>
ที่ได้flatMap()
: ยอมรับ lambda และสร้างProvider<S>
ด้วย แต่ lambda จะใช้ค่าT
และให้ผลลัพธ์Provider<S>
(แทนที่จะสร้างค่าS
โดยตรง) ใช้ flatMap() เมื่อไม่สามารถระบุ S ได้ในเวลาที่กําหนดค่า และคุณได้รับได้เฉพาะProvider<S>
ในทางปฏิบัติแล้ว หากคุณใช้map()
และได้ผลลัพธ์ประเภทProvider<Provider<S>>
แสดงว่าคุณควรใช้flatMap()
แทนzip()
: ให้คุณรวมอินสแตนซ์Provider
2 อินสแตนซ์เพื่อสร้าง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 เฉพาะเมื่อจําเป็นเท่านั้น คุณสามารถใช้flatMap()
เพื่อเข้าถึงเอาต์พุตของTask
ก่อนที่จะสร้างTask
ซึ่งจะมีประโยชน์หากคุณต้องการใช้เอาต์พุตเป็นอินพุตสำหรับอินสแตนซ์Task
อื่นๆ
ผู้ให้บริการและวิธีการเปลี่ยนรูปแบบของผู้ให้บริการมีความสําคัญในการตั้งค่าอินพุตและเอาต์พุตของงานในลักษณะที่ไม่ต้องสร้างงานทั้งหมดล่วงหน้าและแก้ไขค่า
นอกจากนี้ ผู้ให้บริการยังมีข้อมูลการอ้างอิงของงานด้วย เมื่อคุณสร้าง 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, อาร์ติแฟกต์ และ Tasks ในเอกสารนี้
เฟสการสร้าง Gradle
การสร้างโปรเจ็กต์เป็นกระบวนการที่ซับซ้อนและต้องใช้ทรัพยากรโดยเนื้อแท้ และยังมีฟีเจอร์ต่างๆ เช่น การหลีกเลี่ยงการกำหนดค่างาน การตรวจสอบข้อมูลล่าสุด และฟีเจอร์การแคชการกำหนดค่า ซึ่งช่วยประหยัดเวลาในการคํานวณที่ซ้ำกันหรือไม่จําเป็น
หากต้องการใช้การเพิ่มประสิทธิภาพเหล่านี้ สคริปต์และปลั๊กอิน Gradle ต้องปฏิบัติตามกฎที่เข้มงวดในแต่ละระยะการสร้าง Gradle ซึ่งได้แก่ ระยะเริ่มต้น การกำหนดค่า และการดำเนินการ ในคู่มือนี้ เราจะมุ่งเน้นไปที่การกำหนดค่าและระยะการดําเนินการ ดูข้อมูลเพิ่มเติมเกี่ยวกับระยะต่างๆ ทั้งหมดได้ในคู่มือวงจรการสร้าง Gradle
ระยะการกําหนดค่า
ในระหว่างขั้นตอนการกําหนดค่า ระบบจะประเมินสคริปต์บิลด์สําหรับโปรเจ็กต์ทั้งหมดที่เป็นส่วนหนึ่งของบิลด์ ใช้ปลั๊กอิน และแก้ไขข้อกําหนดของบิลด์ ควรใช้ระยะนี้เพื่อกำหนดค่าบิลด์โดยใช้ออบเจ็กต์ DSL และเพื่อลงทะเบียนงานและอินพุตแบบล่าช้า
เนื่องจากเฟสการกําหนดค่าจะทํางานอยู่เสมอ ไม่ว่าระบบจะขอให้เรียกใช้งานใดก็ตาม จึงมีความสําคัญอย่างยิ่งที่จะต้องทําให้เฟสนี้ทำงานได้อย่างรวดเร็วและจำกัดการคำนวณไม่ให้ขึ้นอยู่กับอินพุตอื่นนอกเหนือจากสคริปต์บิลด์
กล่าวคือ คุณไม่ควรเรียกใช้โปรแกรมภายนอกหรืออ่านจากเครือข่าย หรือดำเนินการคํานวณที่ใช้เวลานานซึ่งสามารถเลื่อนไปไว้ที่ระยะการดําเนินการเป็นอินสแตนซ์Task
ที่เหมาะสม
ระยะการดําเนินการ
ในเฟสการดำเนินการ ระบบจะดำเนินการกับงานที่ขอและงานที่ขึ้นอยู่กับงานดังกล่าว กล่าวโดยละเอียดคือ ระบบจะเรียกใช้เมธอดของคลาส Task
ที่มีเครื่องหมาย @TaskAction
ในระหว่างการดําเนินการของงาน คุณจะอ่านข้อมูลจากอินพุต (เช่น ไฟล์) และแก้ไขผู้ให้บริการแบบ Lazy ได้โดยเรียกใช้ Provider<T>.get()
การแก้ไขผู้ให้บริการแบบ Lazy ด้วยวิธีนี้จะเริ่มต้นการเรียก map()
หรือ flatMap()
ตามลำดับตามข้อมูลการพึ่งพางานที่อยู่ในผู้ให้บริการ ระบบจะเรียกใช้งานแบบ Lazy เพื่อแสดงค่าที่จำเป็น
API, อาร์ติแฟกต์ และ Tasks ของตัวแปร
Variant API เป็นกลไกส่วนขยายในปลั๊กอิน Gradle ของ Android ที่ช่วยให้คุณจัดการตัวเลือกต่างๆ ซึ่งปกติจะตั้งค่าโดยใช้ DSL ในไฟล์การกําหนดค่าบิลด์ที่ส่งผลต่อบิลด์ Android ได้ Variant API ยังให้คุณเข้าถึงอาร์ติแฟกต์ขั้นกลางและขั้นสุดท้ายที่บิลด์สร้างขึ้น เช่น ไฟล์คลาส, ไฟล์ Manifest ที่ผสาน หรือไฟล์ APK/AAB
ขั้นตอนการบิลด์ Android และจุดขยาย
เมื่อโต้ตอบกับ AGP ให้ใช้จุดขยายที่สร้างขึ้นเป็นพิเศษแทนการลงทะเบียนการเรียกกลับวงจรชีวิตของ Gradle ทั่วไป (เช่น afterEvaluate()
) หรือการตั้งค่าการพึ่งพา Task
ที่ชัดเจน งานที่สร้างโดย AGP ถือเป็นรายละเอียดการใช้งานและไม่เปิดเผยเป็น API สาธารณะ คุณต้องหลีกเลี่ยงการพยายามรับอินสแตนซ์ของออบเจ็กต์ Task
หรือคาดเดาชื่อ Task
และเพิ่ม Callback หรือการ Dependency ไปยังออบเจ็กต์ Task
เหล่านั้นโดยตรง
AGP จะทําตามขั้นตอนต่อไปนี้เพื่อสร้างและเรียกใช้อินสแตนซ์ Task
ซึ่งจะสร้างอาร์ติแฟกต์การสร้าง ขั้นตอนหลักที่เกี่ยวข้องกับการสร้างออบเจ็กต์ Variant
ตามด้วย Callback ที่ช่วยให้คุณเปลี่ยนแปลงออบเจ็กต์บางรายการที่สร้างขึ้นเป็นส่วนหนึ่งของบิลด์ได้ โปรดทราบว่าการเรียกกลับทั้งหมดจะเกิดขึ้นในระยะการกําหนดค่า (อธิบายไว้ในหน้านี้) และต้องทํางานอย่างรวดเร็ว โดยเลื่อนงานที่ซับซ้อนไปยังอินสแตนซ์ Task
ที่เหมาะสมในเฟสการดําเนินการแทน
- การแยกวิเคราะห์ DSL: ขั้นตอนนี้จะประเมินสคริปต์บิลด์ และสร้างและตั้งค่าพร็อพเพอร์ตี้ต่างๆ ของออบเจ็กต์ DSL ของ Android จากบล็อก
android
นอกจากนี้ ระบบจะลงทะเบียนการเรียกกลับ Variant API ที่อธิบายไว้ในส่วนต่อไปนี้ด้วย finalizeDsl()
: การเรียกกลับที่ช่วยให้คุณเปลี่ยนออบเจ็กต์ DSL ได้ก่อนที่จะมีการล็อกออบเจ็กต์ดังกล่าวไม่ให้สร้างคอมโพเนนต์ (ตัวแปร) ระบบจะสร้างออบเจ็กต์VariantBuilder
โดยอิงตามข้อมูลที่มีอยู่ในออบเจ็กต์ DSLการล็อก DSL: ขณะนี้ระบบล็อก DSL และไม่สามารถเปลี่ยนแปลงได้
beforeVariants()
: Callback นี้อาจส่งผลต่อการสร้างคอมโพเนนต์และพร็อพเพอร์ตี้บางรายการผ่านVariantBuilder
แต่ยังคงอนุญาตให้แก้ไขขั้นตอนการสร้างและอาร์ติแฟกต์ที่สร้างขึ้นการสร้างตัวแปร: รายการคอมโพเนนต์และอาร์ติแฟกต์ที่จะสร้างขึ้นได้รับการสรุปแล้วและไม่สามารถเปลี่ยนแปลงได้
onVariants()
: ใน Callback นี้ คุณจะเข้าถึงออบเจ็กต์Variant
ที่สร้างขึ้นและกําหนดค่าหรือผู้ให้บริการสําหรับค่าProperty
ที่มีเพื่อคํานวณแบบ Lazy Loading ได้การล็อกตัวแปร: ตอนนี้ออบเจ็กต์ตัวแปรจะล็อกอยู่และคุณจะเปลี่ยนแปลงไม่ได้อีกต่อไป
งานที่สร้างขึ้น: ระบบจะใช้ออบเจ็กต์
Variant
และค่าProperty
เพื่อสร้างอินสแตนซ์Task
ที่จําเป็นสําหรับการสร้าง
AGP เปิดตัว AndroidComponentsExtension
ที่ช่วยให้คุณลงทะเบียนการเรียกกลับสําหรับ 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 เพื่อดูวิธีสร้างปลั๊กอินในโปรเจ็กต์ได้ด้วย ตัวอย่างการลงทะเบียนการเรียกกลับจากโค้ดปลั๊กอินมีดังนี้
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
...
}
}
}
มาดูรายละเอียดของคอลแบ็กที่ใช้ได้และประเภทกรณีการใช้งานที่ปลั๊กอินรองรับในแต่ละรายการกัน
finalizeDsl(callback: (DslExtensionT) -> Unit)
ในการเรียกกลับนี้ คุณจะเข้าถึงและแก้ไขออบเจ็กต์ DSL ที่สร้างขึ้นโดยการแยกวิเคราะห์ข้อมูลจากบล็อก android
ในไฟล์บิลด์ได้
ระบบจะใช้ออบเจ็กต์ DSL เหล่านี้เพื่อเริ่มต้นและกำหนดค่าตัวแปรในระยะถัดไปของการสร้าง ตัวอย่างเช่น คุณสามารถสร้างการกำหนดค่าใหม่หรือลบล้างพร็อพเพอร์ตี้แบบเป็นโปรแกรมได้ แต่โปรดทราบว่าค่าทั้งหมดต้องได้รับการแก้ไข ณ เวลาการกําหนดค่า จึงต้องไม่ใช้อินพุตภายนอก
หลังจากการเรียกกลับนี้ดำเนินการเสร็จแล้ว ออบเจ็กต์ 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
เมื่อการเรียกใช้ Callback beforeVariants()
เสร็จสิ้น
androidComponents {
beforeVariants { variantBuilder ->
variantBuilder.minSdk = 23
}
}
ฟังก์ชัน Callback ของ beforeVariants()
จะรับ VariantSelector
(ไม่บังคับ) ซึ่งคุณรับได้จากเมธอด selector()
ใน androidComponentsExtension
ซึ่งคุณสามารถใช้เพื่อกรองคอมโพเนนต์ที่เข้าร่วมการเรียกใช้ Callback ตามชื่อ ประเภทบิลด์ หรือรสชาติของผลิตภัณฑ์ได้
androidComponents {
beforeVariants(selector().withName("adfree")) { variantBuilder ->
variantBuilder.minSdk = 23
}
}
onVariants()
เมื่อเรียกใช้ onVariants()
ระบบจะตัดสินใจเกี่ยวกับอาร์ติแฟกต์ทั้งหมดที่จะสร้างโดย AGPT ไว้แล้ว คุณจึงปิดใช้อาร์ติแฟกต์เหล่านั้นไม่ได้อีก อย่างไรก็ตาม คุณสามารถแก้ไขค่าบางอย่างที่ใช้สำหรับงานได้โดยกำหนดค่าสำหรับแอตทริบิวต์ Property
ในออบเจ็กต์ Variant
เนื่องจากค่า Property
จะได้รับการแก้ไขเมื่อมีการทำงานของ AGP เท่านั้น คุณจึงเชื่อมต่อค่ากับผู้ให้บริการจากงานที่คุณกำหนดเองได้อย่างปลอดภัย ซึ่งจะดำเนินการคำนวณที่จำเป็น รวมถึงการอ่านจากอินพุตภายนอก เช่น ไฟล์หรือเครือข่าย
// 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()
จากนั้นเครื่องมือชุดค่าผสมของ 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
Cardinality ของ Artifact
แสดงจํานวนอินสแตนซ์ FileSystemLocation
หรือจํานวนไฟล์หรือไดเรกทอรีของประเภทอาร์ติแฟกต์ คุณสามารถรับข้อมูลเกี่ยวกับ Cardinality ของอาร์ติแฟกต์ได้โดยตรวจสอบคลาสหลักของอาร์ติแฟกต์ อาร์ติแฟกต์ที่มี 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
ที่มีอาร์ติแฟกต์เวอร์ชันสุดท้าย (หลังการดำเนินการทั้งหมดเสร็จสิ้น)
ปลั๊กอินหลายรายการสามารถเพิ่มการดำเนินการกับอาร์ติแฟกต์ลงในไปป์ไลน์ได้เท่าใดก็ได้จาก onVariants()
callback และ AGP จะตรวจสอบว่ามีการเชื่อมโยงอย่างถูกต้องเพื่อให้งานทั้งหมดทำงานในเวลาที่เหมาะสม และสร้างและอัปเดตอาร์ติแฟกต์อย่างถูกต้อง ซึ่งหมายความว่าเมื่อการดำเนินการเปลี่ยนเอาต์พุตโดยการเพิ่ม แทนที่ หรือเปลี่ยนรูปแบบเอาต์พุต การดำเนินการถัดไปจะเห็นเวอร์ชันอัปเดตของอาร์ติแฟกต์เหล่านี้เป็นอินพุต เป็นต้น
จุดแรกเข้าในการลงทะเบียนการดำเนินการคือคลาส Artifacts
ข้อมูลโค้ดต่อไปนี้แสดงวิธีเข้าถึงอินสแตนซ์ของ Artifacts
จากพร็อพเพอร์ตี้บนออบเจ็กต์ Variant
ใน onVariants()
callback
จากนั้นคุณสามารถส่ง TaskProvider
ที่กําหนดเองเพื่อรับออบเจ็กต์ TaskBasedOperation
(1) และใช้เพื่อเชื่อมต่ออินพุตและเอาต์พุตโดยใช้เมธอด wiredWith*
อย่างใดอย่างหนึ่ง (2)
วิธีที่แน่นอนที่คุณต้องเลือกขึ้นอยู่กับ Cardinality และประเภท 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
ที่จะทํางานกับชุดค่าผสมอื่นๆ ของ Cardinality Artifact
และประเภท FileSystemLocation
หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับการขยาย AGP เราขอแนะนําให้อ่านส่วนต่อไปนี้จากคู่มือระบบการบิลด์ Gradle
- การพัฒนาปลั๊กอิน Gradle ที่กำหนดเอง
- การใช้ปลั๊กอิน Gradle
- การพัฒนาประเภทงาน Gradle ที่กำหนดเอง
- การกำหนดค่าแบบ Lazy
- การหลีกเลี่ยงการกำหนดค่างาน