เขียนปลั๊กอิน Gradle

ปลั๊กอิน Android Gradle (AGP) เป็นระบบบิลด์อย่างเป็นทางการสำหรับแอปพลิเคชัน Android รวมถึงรองรับการคอมไพล์แหล่งที่มาหลายประเภทและลิงก์เข้าด้วยกันเป็นแอปพลิเคชันที่คุณสามารถเรียกใช้ได้บนอุปกรณ์ Android จริงหรือโปรแกรมจำลอง

AGP มีจุดขยายสำหรับปลั๊กอินเพื่อควบคุมอินพุตการสร้างและขยายฟังก์ชันการทำงานผ่านขั้นตอนใหม่ที่ผสานรวมกับงานการสร้างมาตรฐานได้ AGP เวอร์ชันก่อนหน้าไม่มี API อย่างเป็นทางการที่แยกออกจากการใช้งานภายในอย่างชัดเจน ตั้งแต่เวอร์ชัน 7.0 AGP มีชุดAPI อย่างเป็นทางการที่เสถียรซึ่งคุณไว้วางใจได้

วงจรชีวิตของ AGP API

AGP เป็นไปตามวงจรของฟีเจอร์ Gradle เพื่อกำหนดสถานะของ API ดังนี้

  • ภายใน: ไม่ได้มีไว้สำหรับการใช้งานสาธารณะ
  • กำลังพัฒนา: พร้อมให้ใช้งานแบบสาธารณะแต่ยังไม่เป็นเวอร์ชันสุดท้าย ซึ่งหมายความว่าอาจใช้งานร่วมกับเวอร์ชันเก่าไม่ได้ในเวอร์ชันสุดท้าย
  • สาธารณะ: พร้อมให้ใช้งานแบบสาธารณะและมีความเสถียร
  • เลิกใช้งานแล้ว: ระบบไม่รองรับอีกต่อไปและมี API ใหม่เข้ามาแทนที่

นโยบายการเลิกใช้งาน

AGP กำลังพัฒนาไปพร้อมกับการเลิกใช้งาน API เก่าและแทนที่ด้วย API ใหม่ที่เสถียรและภาษาเฉพาะโดเมน (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 หลักที่เกี่ยวข้องกับการดําเนินการแบบเลื่อนเวลาและเมธอดหลัก

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 นั้นจะกลายเป็นข้อกำหนดโดยนัยของ 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 เชื่อมต่อกับงานอื่นและค่าของ ManifestProducerTask กลายเป็นค่าที่ต้องระบุ

แม้ว่าคุณจะเขียนงานที่กำหนดเองซึ่งใช้อินพุตและ/หรือสร้างเอาต์พุต แต่ AGP ไม่ได้ให้สิทธิ์เข้าถึงแบบสาธารณะสำหรับงานของตัวเองโดยตรง รายละเอียดเหล่านี้เป็นรายละเอียดการใช้งานที่อาจมีการเปลี่ยนแปลงในแต่ละเวอร์ชัน แต่ AGP จะเสนอ Variant API และสิทธิ์เข้าถึงเอาต์พุตของงาน หรืออาร์ติแฟกต์การสร้างที่คุณสามารถอ่านและเปลี่ยนรูปแบบได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับVariant API, อาร์ติแฟกต์ และ Tasks ในเอกสารนี้

ระยะการทำงานของ Gradle

การสร้างโปรเจ็กต์เป็นกระบวนการที่ซับซ้อนและต้องใช้ทรัพยากรโดยเนื้อแท้ และยังมีฟีเจอร์ต่างๆ เช่น การหลีกเลี่ยงการกำหนดค่างาน การตรวจสอบข้อมูลล่าสุด และฟีเจอร์การแคชการกำหนดค่า ซึ่งช่วยประหยัดเวลาในการคํานวณที่ซ้ำกันหรือไม่จําเป็น

หากต้องการใช้การเพิ่มประสิทธิภาพเหล่านี้ สคริปต์และปลั๊กอิน Gradle ต้องปฏิบัติตามกฎที่เข้มงวดในแต่ละระยะการสร้าง Gradle ซึ่งได้แก่ ระยะเริ่มต้น การกำหนดค่า และการดำเนินการ ในคู่มือนี้ เราจะมุ่งเน้นไปที่การกำหนดค่าและระยะการดําเนินการ ดูข้อมูลเพิ่มเติมเกี่ยวกับระยะต่างๆ ทั้งหมดได้ในคู่มือวงจรการสร้าง Gradle

ระยะการกําหนดค่า

ในระหว่างขั้นตอนการกําหนดค่า ระบบจะประเมินสคริปต์บิลด์สําหรับโปรเจ็กต์ทั้งหมดที่เป็นส่วนหนึ่งของบิลด์ ใช้ปลั๊กอิน และแก้ไขข้อกําหนดของบิลด์ ควรใช้ระยะนี้เพื่อกำหนดค่าบิลด์โดยใช้ออบเจ็กต์ DSL และเพื่อลงทะเบียนงานและอินพุตแบบล่าช้า

เนื่องจากเฟสการกําหนดค่าจะทํางานอยู่เสมอ ไม่ว่าระบบจะขอให้เรียกใช้งานใดก็ตาม จึงมีความสําคัญอย่างยิ่งที่จะต้องทําให้เฟสนี้ทำงานได้อย่างรวดเร็วและจำกัดการคำนวณไม่ให้ขึ้นอยู่กับอินพุตอื่นนอกเหนือจากสคริปต์บิลด์ กล่าวคือ คุณไม่ควรเรียกใช้โปรแกรมภายนอกหรืออ่านจากเครือข่าย หรือดำเนินการคํานวณที่ใช้เวลานานซึ่งสามารถเลื่อนไปไว้ในระยะการดําเนินการเป็นอินสแตนซ์Taskที่เหมาะสม

ระยะการดําเนินการ

ในเฟสการดำเนินการ ระบบจะดำเนินการกับงานที่ขอและงานที่ขึ้นอยู่กับงานดังกล่าว กล่าวโดยละเอียดคือ ระบบจะเรียกใช้เมธอดของคลาส Task ที่มีเครื่องหมาย @TaskAction ในระหว่างการดำเนินการของงาน คุณจะได้รับอนุญาตให้อ่านจากอินพุต (เช่น ไฟล์) และแก้ไขผู้ให้บริการแบบ Lazy โดยเรียกใช้ Provider<T>.get() การแก้ไขผู้ให้บริการแบบ Lazy ด้วยวิธีนี้จะเริ่มต้นการเรียก map() หรือ flatMap() ตามลำดับตามข้อมูลการพึ่งพางานที่อยู่ในผู้ให้บริการ ระบบจะเรียกใช้งานแบบ Lazy เพื่อแสดงค่าที่จำเป็น

Variant API, อาร์ติแฟกต์ และงาน

Variant API เป็นกลไกส่วนขยายในปลั๊กอิน Gradle ของ Android ที่ช่วยให้คุณจัดการตัวเลือกต่างๆ ซึ่งปกติจะตั้งค่าโดยใช้ DSL ในไฟล์การกําหนดค่าบิลด์ที่ส่งผลต่อบิลด์ Android ได้ Variant API ยังให้คุณเข้าถึงอาร์ติแฟกต์ขั้นกลางและขั้นสุดท้ายที่บิลด์สร้างขึ้น เช่น ไฟล์คลาส, ไฟล์ Manifest ที่ผสาน หรือไฟล์ APK/AAB

ขั้นตอนการบิลด์ Android และจุดขยาย

เมื่อโต้ตอบกับ AGP ให้ใช้จุดขยายที่สร้างขึ้นเป็นพิเศษแทนการลงทะเบียนการเรียกกลับวงจรชีวิตของ Gradle ทั่วไป (เช่น afterEvaluate()) หรือการตั้งค่าการพึ่งพา Task ที่ชัดเจน งานที่สร้างโดย AGP จะถือเป็นรายละเอียดการใช้งานและจะไม่แสดงเป็น API สาธารณะ คุณต้องหลีกเลี่ยงการพยายามรับอินสแตนซ์ของออบเจ็กต์ Task หรือการคาดเดาชื่อ Task และการเพิ่มการเรียกกลับหรือข้อมูลที่ต้องพึ่งพาไปยังออบเจ็กต์ Task เหล่านั้นโดยตรง

AGP จะทําตามขั้นตอนต่อไปนี้เพื่อสร้างและเรียกใช้อินสแตนซ์ Task ซึ่งจะสร้างอาร์ติแฟกต์การสร้าง ขั้นตอนหลักที่เกี่ยวข้องกับการสร้างออบเจ็กต์ Variant ตามด้วยคอลแบ็กที่ช่วยให้คุณทำการเปลี่ยนแปลงออบเจ็กต์บางอย่างที่สร้างเป็นส่วนหนึ่งของบิลด์ได้ โปรดทราบว่าการเรียกกลับทั้งหมดจะเกิดขึ้นในระยะการกําหนดค่า (อธิบายไว้ในหน้านี้) และต้องทํางานอย่างรวดเร็ว โดยเลื่อนงานที่ซับซ้อนไปยังอินสแตนซ์ Task ที่เหมาะสมในเฟสการดําเนินการแทน

  1. การแยกวิเคราะห์ DSL: ขั้นตอนนี้จะประเมินสคริปต์บิลด์ และสร้างและตั้งค่าพร็อพเพอร์ตี้ต่างๆ ของออบเจ็กต์ DSL ของ Android จากบล็อก android นอกจากนี้ ระบบจะลงทะเบียนการเรียกกลับ Variant API ที่อธิบายไว้ในส่วนต่อไปนี้ด้วย
  2. finalizeDsl(): การเรียกกลับที่ช่วยให้คุณเปลี่ยนออบเจ็กต์ DSL ได้ก่อนที่จะมีการล็อกออบเจ็กต์ดังกล่าวไม่ให้สร้างคอมโพเนนต์ (ตัวแปร) ระบบจะสร้างออบเจ็กต์ VariantBuilder ตามข้อมูลที่อยู่ในออบเจ็กต์ DSL

  3. การล็อก DSL: DSL ล็อกอยู่และเปลี่ยนแปลงไม่ได้อีกต่อไป

  4. beforeVariants(): การคอลแบ็กนี้อาจส่งผลต่อคอมโพเนนต์ที่จะสร้างขึ้นและพร็อพเพอร์ตี้บางอย่างของคอมโพเนนต์ผ่าน VariantBuilder แต่ยังคงอนุญาตให้แก้ไขขั้นตอนการสร้างและอาร์ติแฟกต์ที่สร้างขึ้น

  5. การสร้างตัวแปร: รายการคอมโพเนนต์และอาร์ติแฟกต์ที่จะสร้างขึ้นจะได้รับการสรุปแล้วและไม่สามารถเปลี่ยนแปลงได้

  6. onVariants(): ในคอลแบ็กนี้ คุณจะได้รับสิทธิ์เข้าถึงออบเจ็กต์ Variant ที่สร้างขึ้น และสามารถตั้งค่าหรือผู้ให้บริการสำหรับค่า Property ที่อยู่ในออบเจ็กต์เพื่อที่จะได้คํานวณแบบล่าช้า

  7. การล็อกตัวแปร: ตอนนี้ออบเจ็กต์ตัวแปรจะล็อกอยู่และคุณจะเปลี่ยนแปลงไม่ได้อีกต่อไป

  8. งานที่สร้างขึ้น: ระบบจะใช้ออบเจ็กต์ 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 ต้องไม่ได้รับการแก้ไขเมื่อการเรียกกลับ beforeVariants() เสร็จสิ้น

androidComponents {
    beforeVariants { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

ฟังก์ชัน Callback ของ beforeVariants() จะรับ VariantSelector (ไม่บังคับ) ซึ่งคุณรับได้จากเมธอด selector() ใน androidComponentsExtension คุณสามารถใช้ตัวกรองนี้เพื่อกรองคอมโพเนนต์ที่เข้าร่วมการเรียกใช้การเรียกกลับตามชื่อ ประเภทบิลด์ หรือตัวแปรผลิตภัณฑ์

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 ประเภท เช่น

ดูรายการแหล่งที่มาทั้งหมดที่เพิ่มได้ได้ที่ 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 นั้นตามต้องการและแสดงผล 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