เขียนปลั๊กอิน 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 ที่เลิกใช้งานแล้วออกจาก 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 ไปไว้ในช่วงหลังของการสร้าง ซึ่งเป็นแกนหลักของ 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> ที่ได้
  • 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 Loading เมื่อจำเป็นเท่านั้น คุณใช้ flatMap() เพื่อเข้าถึงเอาต์พุตของ Task ก่อนสร้าง Task ได้ ซึ่งอาจเป็นประโยชน์หากคุณต้องการใช้เอาต์พุตเป็นอินพุตไปยังอินสแตนซ์ Task อื่นๆ

ผู้ให้บริการและวิธีการเปลี่ยนรูปแบบมีความสําคัญอย่างยิ่งสําหรับการตั้งค่าอินพุตและเอาต์พุตของงานในลักษณะ Lazy Loading กล่าวคือ ไม่จำเป็นต้องสร้างงานทั้งหมดล่วงหน้าและแก้ไขค่า

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

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

เฟสการสร้าง Gradle

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

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

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

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

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

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

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

API, อาร์ติแฟกต์ และ Tasks ของตัวแปร

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

ขั้นตอนของบิลด์และส่วนขยายของ Android

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

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

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

  3. การล็อก DSL: ขณะนี้ระบบล็อก DSL และไม่สามารถเปลี่ยนแปลงได้

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

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

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

  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 เพื่อดูวิธีสร้างปลั๊กอินในโปรเจ็กต์ได้ด้วย ต่อไปนี้คือตัวอย่างของการลงทะเบียน Callback จากโค้ดปลั๊กอิน

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 เหล่านี้เพื่อเริ่มต้นและกำหนดค่าตัวแปรในระยะถัดไปของการสร้าง เช่น คุณอาจสร้างการกำหนดค่าใหม่หรือลบล้างพร็อพเพอร์ตี้โดยใช้โปรแกรม แต่โปรดทราบว่าค่าทั้งหมดต้องได้รับการแก้ไข ณ เวลาที่กำหนดค่า ค่าดังกล่าวต้องไม่พึ่งอินพุตภายนอกใดๆ หลังจาก 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 เมื่อการเรียกใช้ 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 ประเภท เช่น

ดูรายการแหล่งที่มาทั้งหมดที่เพิ่มได้ได้ที่ 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 หรือจํานวนไฟล์หรือไดเรกทอรีของประเภทอาร์ติแฟกต์ คุณสามารถรับข้อมูลเกี่ยวกับ 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 ที่จะใช้งานได้กับชุดค่าผสมอื่นๆ ของประเภท Artifact Cardinality และ FileSystemLocation

หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับการขยาย AGP ขอแนะนำให้อ่านส่วนต่อไปนี้จากคู่มือระบบบิลด์ Gradle