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

ปลั๊กอิน 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 ที่เลิกใช้งานแล้วจะถูกนำออกจาก AGP ใน API หลักถัดไป ตัวอย่างเช่น หากเลิกใช้งาน API ใน AGP 7.0 ก็จะพร้อมให้บริการ ในเวอร์ชันนั้นและสร้างคำเตือน API นั้นจะไม่มีให้ใช้ใน AGP 8.0

หากต้องการดูตัวอย่าง API ใหม่ที่ใช้ในการปรับแต่งบิลด์ทั่วไป โปรดดู ที่สูตรอาหารสำหรับปลั๊กอิน Android Gradle โดยจะมีตัวอย่างการปรับแต่งบิลด์ที่พบบ่อย คุณยังหาข้อมูลเพิ่มเติม รายละเอียดเกี่ยวกับ API ใหม่ใน เอกสารอ้างอิง

ข้อมูลเบื้องต้นเกี่ยวกับบิลด์ของ Gradle

คู่มือนี้ไม่ได้ครอบคลุมระบบบิลด์ Gradle ทั้งหมด แต่ก็ครอบคลุม ชุดแนวคิดขั้นต่ำที่จำเป็นเพื่อช่วยคุณผสานรวมกับ API ของเรา และ ไปยังเอกสารหลักของ Gradle เพื่ออ่านข้อมูลเพิ่มเติม

เราสันนิษฐานถึงความรู้พื้นฐานเกี่ยวกับวิธีการทำงานของ Gradle ซึ่งรวมถึงวิธีกำหนดค่า แก้ไขไฟล์บิลด์ ใช้ปลั๊กอิน และเรียกใช้งานต่างๆ หากต้องการเรียนรู้เกี่ยวกับ ข้อมูลเบื้องต้นเกี่ยวกับ Gradle ในเรื่อง AGP เราขอแนะนำให้คุณอ่านหัวข้อกำหนดค่า บิลด์ เพื่อเรียนรู้เกี่ยวกับเฟรมเวิร์กทั่วไปสำหรับการปรับแต่ง ปลั๊กอิน Gradle ดู การพัฒนาปลั๊กอิน Gradle ที่กำหนดเอง

อภิธานศัพท์เกี่ยวกับประเภท Lazy ของ Gradle

Gradle มีหลายประเภทที่ทำงานได้ "แบบ Lazy Loading" หรือช่วยเลื่อนของหนัก การคำนวณหรือ Task ไปจนถึงช่วงหลังๆ ของการสร้าง ประเภทเหล่านี้เป็นหัวใจสำคัญของ Gradle และ AGP API รายการต่อไปนี้มีประเภท Gradle หลักที่เกี่ยวข้อง ในการดำเนินการแบบ Lazy Loading และวิธีการหลัก

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 ด้วย ต่างจาก Provider<T> ซึ่งเป็นแบบอ่านอย่างเดียว คุณยังสามารถตั้งค่าสำหรับ Property<T> โดยมี 2 วิธีดังนี้
  • ตั้งค่าประเภท T โดยตรงเมื่อพร้อมใช้งาน โดยไม่ต้องใช้ การคำนวณที่มีการเลื่อนเวลา
  • กำหนด Provider<T> อีกรายการเป็นแหล่งที่มาของค่าของ Property<T> ใน ในกรณีนี้ ค่า T จะเป็นรูปธรรมเมื่อ Property.get() เป็น โทรออก
TaskProvider
นำ Provider<Task> มาใช้ หากต้องการสร้าง TaskProvider ให้ใช้ tasks.register() ไม่ใช่ tasks.create() เพื่อให้มั่นใจว่างานจะมีการสร้างอินสแตนซ์เท่านั้น ขี้เกียจเมื่อจำเป็น คุณสามารถใช้ flatMap() เพื่อเข้าถึงเอาต์พุตของ Task ก่อนที่จะสร้าง Task ซึ่งจะเป็นประโยชน์หากคุณต้องการใช้ เอาต์พุตเป็นอินพุตไปยังอินสแตนซ์ Task อื่นๆ

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

นอกจากนี้ ผู้ให้บริการยังมีข้อมูลเกี่ยวกับทรัพยากร Dependency ของงานด้วย เมื่อคุณสร้าง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 แทน มี API เวอร์ชันแปรผันและสิทธิ์เข้าถึงเอาต์พุตของงาน หรือสร้าง ที่คุณสามารถอ่านและเปลี่ยนรูปแบบได้ โปรดดู API, อาร์ติแฟกต์ และ Tasks ของตัวแปรใน เพื่อดูข้อมูลเพิ่มเติม

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 ต้องไม่ แก้ไขเมื่อการเรียกใช้ Callback beforeVariants() เสร็จสิ้น

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

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

androidComponents {
    beforeVariants(selector().withName("adfree")) { variantBuilder ->
        variantBuilder.minSdk = 23
    }
}

onVariants()

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

มอบแหล่งที่มาที่สร้างขึ้นลงในบิลด์

ปลั๊กอินอาจสร้างแหล่งที่มาประเภทต่างๆ ที่สร้างขึ้นได้ เช่น

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