ปลั๊กอิน 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: มี API อย่างเป็นทางการของ AGP รวมถึง API ที่เสถียร (เช่นAndroidComponentsExtensionVariantและอินเทอร์เฟซ 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 และระบุคลาสรันไทม์ในคลาสพาธของ buildscript การใช้การกำหนดค่า compileOnly จะป้องกันไม่ให้ปลั๊กอินที่เผยแพร่รั่วไหลทรัพยากร Dependency รันไทม์ใน AGP เวอร์ชันที่เฉพาะเจาะจง และหลีกเลี่ยงความขัดแย้งด้านเวอร์ชันสำหรับผู้บริโภค หากคุณ
พัฒนาตรรกะการสร้างภายใน เช่น ปลั๊กอินในไดเรกทอรี buildSrc หรือ
ปลั๊กอินตามรูปแบบในบิลด์แบบคอมโพสิต ให้ใช้ตัวเลือกการกำหนดค่า implementation แทน ปลั๊กอินเหล่านี้จะทำงานภายในบิลด์และต้องใช้ AGP ในเส้นทางคลาสของรันไทม์
ข้อมูลพื้นฐานเกี่ยวกับการสร้าง Gradle
คู่มือนี้ไม่ได้ครอบคลุมระบบบิลด์ Gradle ทั้งหมด อย่างไรก็ตาม เอกสารนี้ครอบคลุม ชุดแนวคิดที่จำเป็นขั้นต่ำเพื่อช่วยให้คุณผสานรวมกับ API ของเรา และ ลิงก์ไปยังเอกสารประกอบหลักของ Gradle เพื่ออ่านเพิ่มเติม
เราถือว่าคุณมีความรู้พื้นฐานเกี่ยวกับวิธีการทำงานของ Gradle รวมถึงวิธีกำหนดค่า โปรเจ็กต์ แก้ไขไฟล์บิลด์ ใช้ปลั๊กอิน และเรียกใช้ทาสก์ หากต้องการดูข้อมูลพื้นฐานเกี่ยวกับ Gradle ที่เกี่ยวข้องกับ AGP เราขอแนะนำให้ดูกำหนดค่าบิลด์ ดูข้อมูลเกี่ยวกับเฟรมเวิร์กทั่วไปสำหรับการปรับแต่ง ปลั๊กอิน Gradle ได้ที่ การพัฒนาปลั๊กอิน Gradle ที่กำหนดเอง
คำศัพท์เกี่ยวกับประเภท Lazy ของ Gradle
Gradle มีหลายประเภทที่ทำงานแบบ "เลซี่" หรือช่วยเลื่อนการคำนวณที่ซับซ้อนหรือTask
การสร้างไปยังระยะต่อๆ ไปของบิลด์ ประเภทเหล่านี้เป็นหัวใจสำคัญของ API ของ Gradle และ AGP หลายรายการ
รายการต่อไปนี้มีประเภท 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>ที่ได้ ทำให้ทั้งเชนเป็นแบบ 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()เพื่อให้มั่นใจว่าระบบจะสร้างอินสแตนซ์ของงาน แบบเลื่อนเวลาเมื่อจำเป็นเท่านั้น คุณใช้flatMap()เพื่อเข้าถึงเอาต์พุตของTaskก่อนที่จะสร้างTaskได้ ซึ่งอาจเป็นประโยชน์หากคุณต้องการใช้ เอาต์พุตเป็นอินพุตไปยังอินสแตนซ์Taskอื่นๆ
ผู้ให้บริการและวิธีการแปลงข้อมูลเป็นสิ่งจำเป็นสำหรับการตั้งค่าอินพุต และเอาต์พุตของงานในลักษณะที่เลื่อนการประมวลผลออกไป นั่นคือโดยไม่ต้อง สร้างงานทั้งหมดล่วงหน้าและแก้ไขค่า
นอกจากนี้ ผู้ให้บริการยังระบุข้อมูลการขึ้นต่อกันของงานด้วย เมื่อสร้าง Provider โดย
การแปลงเอาต์พุต Task Task จะกลายเป็นทรัพยากร Dependency โดยนัยของ
Provider และจะได้รับการสร้างและเรียกใช้ทุกครั้งที่มีการแก้ไขค่าของ Provider เช่น เมื่อ Task อื่นต้องการ
ต่อไปนี้เป็นตัวอย่างการลงทะเบียน 2 งาน ได้แก่ GitVersionTask และ
ManifestProducerTask ขณะที่เลื่อนการสร้างอินสแตนซ์ Task จนกว่าจะ
จำเป็นต้องใช้จริงๆ ระบบจะตั้งค่าอินพุต ManifestProducerTask เป็น a
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, Artifacts และ Tasks ในเอกสารนี้
ขั้นตอนการบิลด์ Gradle
การสร้างโปรเจ็กต์เป็นกระบวนการที่ซับซ้อนและต้องใช้ทรัพยากรจำนวนมากโดยธรรมชาติ และมีฟีเจอร์ต่างๆ เช่น การหลีกเลี่ยงการกำหนดค่าของงาน การตรวจสอบ ข้อมูลล่าสุด และฟีเจอร์การแคชการกำหนดค่าที่จะช่วยลดเวลาที่ใช้ ในการคำนวณที่ทำซ้ำได้หรือไม่จำเป็น
หากต้องการใช้การเพิ่มประสิทธิภาพบางอย่างเหล่านี้ สคริปต์และปลั๊กอิน Gradle ต้อง ปฏิบัติตามกฎที่เข้มงวดในระหว่างขั้นตอนการสร้าง Gradle ที่แตกต่างกันแต่ละขั้นตอน ได้แก่ การเริ่มต้น การกำหนดค่า และการดำเนินการ ในคู่มือนี้ เราจะมุ่งเน้นไปที่ ขั้นตอนการกำหนดค่าและการดำเนินการ ดูข้อมูลเพิ่มเติมเกี่ยวกับทุกขั้นตอนได้ในคู่มือวงจรการสร้าง Gradle
ระยะการกำหนดค่า
ในระหว่างขั้นตอนการกำหนดค่า ระบบจะประเมินสคริปต์บิลด์สำหรับโปรเจ็กต์ทั้งหมดที่เป็นส่วนหนึ่งของบิลด์ ใช้ปลั๊กอิน และแก้ไขการขึ้นต่อกันของบิลด์ ควรใช้เฟสนี้เพื่อกำหนดค่าบิลด์โดยใช้ออบเจ็กต์ DSL และเพื่อลงทะเบียนงานและอินพุตของงานแบบเลซี
เนื่องจากระยะการกำหนดค่าจะทำงานเสมอ ไม่ว่าระบบจะขอให้งานใดทำงานก็ตาม จึงเป็นสิ่งสำคัญอย่างยิ่งที่จะต้องทำให้ระยะการกำหนดค่ามีประสิทธิภาพและจำกัดการคำนวณไม่ให้ขึ้นอยู่กับอินพุตอื่นนอกเหนือจากสคริปต์บิลด์เอง
กล่าวคือ คุณไม่ควรเรียกใช้โปรแกรมภายนอกหรืออ่านจากเครือข่าย หรือ
ทำการคำนวณที่ใช้เวลานานซึ่งสามารถเลื่อนไปยังระยะการดำเนินการได้ตามความเหมาะสม
Taskอินสแตนซ์
ระยะการดำเนินการ
ในระยะการดำเนินการ ระบบจะดำเนินการกับงานที่ขอและงานที่ขึ้นอยู่กับงานนั้น
โดยเฉพาะอย่างยิ่ง ระบบจะเรียกใช้เมธอดของคลาส Task ที่ทำเครื่องหมายด้วย @TaskAction
ในระหว่างการดำเนินการงาน คุณจะได้รับอนุญาตให้อ่านจากอินพุต (เช่น ไฟล์) และแก้ไขผู้ให้บริการแบบเลซีโดยการเรียก Provider<T>.get() การแก้ไข Lazy Provider ด้วยวิธีนี้จะเริ่มลำดับการเรียก map() หรือ flatMap() ที่เป็นไปตามข้อมูลทรัพยากร Dependency ของงานที่อยู่ใน Provider ระบบจะเรียกใช้งาน
งานแบบเลื่อนเวลาเพื่อสร้างค่าที่จำเป็น
API ของตัวแปร อาร์ติแฟกต์ และงาน
Variant API เป็นกลไกการขยายในปลั๊กอิน Android Gradle ที่ช่วยให้คุณจัดการตัวเลือกต่างๆ ซึ่งโดยปกติจะตั้งค่าโดยใช้ DSL ในไฟล์การกำหนดค่าบิลด์ที่มีผลต่อบิลด์ Android นอกจากนี้ Variant API ยังให้สิทธิ์เข้าถึงอาร์ติแฟกต์ระดับกลางและขั้นสุดท้ายที่สร้างขึ้นโดยบิลด์ เช่น ไฟล์คลาส, Manifest ที่ผสาน หรือไฟล์ APK/AAB
ขั้นตอนการสร้าง Android และจุดขยาย
เมื่อโต้ตอบกับ AGP ให้ใช้จุดขยายที่สร้างขึ้นมาเป็นพิเศษแทน
การลงทะเบียนการเรียกกลับวงจรการทำงานของ Gradle ทั่วไป (เช่น afterEvaluate()) หรือ
การตั้งค่าการขึ้นต่อกันของ Task ที่ชัดเจน งานที่ AGP สร้างขึ้นถือเป็นรายละเอียดการติดตั้งใช้งานและไม่ได้เปิดเผยเป็น API สาธารณะ คุณต้องหลีกเลี่ยง
การพยายามรับอินสแตนซ์ของออบเจ็กต์ Task หรือการคาดเดาชื่อ Task และ
การเพิ่มการเรียกกลับหรือการอ้างอิงไปยังออบเจ็กต์ Task เหล่านั้นโดยตรง
AGP จะทำตามขั้นตอนต่อไปนี้เพื่อสร้างและเรียกใช้อินสแตนซ์ Task ซึ่งจะสร้างอาร์ติแฟกต์บิลด์ ขั้นตอนหลักที่เกี่ยวข้องกับการสร้างออบเจ็กต์
Variant
จะตามด้วย Callback ที่ช่วยให้คุณทำการเปลี่ยนแปลงออบเจ็กต์บางอย่าง
ที่สร้างขึ้นเป็นส่วนหนึ่งของบิลด์ได้ โปรดทราบว่าการเรียกกลับทั้งหมดจะเกิดขึ้นในระยะการกำหนดค่า
(อธิบายไว้ในหน้านี้) และต้องทำงานอย่างรวดเร็ว โดยเลื่อนงานที่ซับซ้อน
ไปยังอินสแตนซ์ Task ที่เหมาะสมในระยะการดำเนินการแทน
- การแยกวิเคราะห์ DSL: กระบวนการนี้จะเกิดขึ้นเมื่อมีการประเมินสคริปต์บิลด์ และเมื่อมีการสร้าง
และตั้งค่าพร็อพเพอร์ตี้ต่างๆ ของออบเจ็กต์ Android DSL จากบล็อก
androidระบบจะลงทะเบียนการเรียกกลับของ Variant API ที่อธิบายไว้ในส่วนต่อไปนี้ในระหว่างขั้นตอนนี้ด้วย finalizeDsl(): การเรียกกลับที่ช่วยให้คุณเปลี่ยนออบเจ็กต์ DSL ก่อนที่จะ ล็อกเพื่อสร้างคอมโพเนนต์ (ตัวแปร)VariantBuilderจะสร้างออบเจ็กต์ ตามข้อมูลที่มีอยู่ในออบเจ็กต์ DSLการล็อก DSL: ตอนนี้ DSL ล็อกอยู่และเปลี่ยนแปลงไม่ได้อีกต่อไป
beforeVariants(): การเรียกกลับนี้สามารถส่งผลต่อคอมโพเนนต์ที่จะสร้าง และพร็อพเพอร์ตี้บางอย่างของคอมโพเนนต์เหล่านั้นผ่านVariantBuilderแต่ยังคงอนุญาตให้ แก้ไขโฟลว์การสร้างและอาร์ติแฟกต์ที่สร้างขึ้นการสร้างรูปแบบ: รายการคอมโพเนนต์และอาร์ติแฟกต์ที่จะสร้างเสร็จสมบูรณ์แล้วและไม่สามารถเปลี่ยนแปลงได้
onVariants(): ในการเรียกกลับนี้ คุณจะได้รับสิทธิ์เข้าถึงออบเจ็กต์Variantที่สร้างขึ้น และสามารถตั้งค่าหรือผู้ให้บริการสำหรับค่าPropertyที่ออบเจ็กต์เหล่านั้นมีอยู่ เพื่อให้ระบบคำนวณค่าแบบเลซีได้การล็อกตัวแปร: ตอนนี้ออบเจ็กต์ตัวแปรล็อกไว้แล้วและไม่สามารถเปลี่ยนแปลงได้อีก
งานที่สร้างขึ้น: ระบบจะใช้ออบเจ็กต์
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 เพื่อดูวิธีสร้างปลั๊กอินในโปรเจ็กต์ได้ด้วย ตรวจสอบว่าปลั๊กอิน
คอมไพล์กับอาร์ติแฟกต์ gradle-api
ในไฟล์บิลด์ ตัวอย่างการลงทะเบียนการเรียกกลับจากโค้ดปลั๊กอิน
มีดังนี้
abstract class ExamplePlugin: Plugin<Project> {
override fun apply(project: Project) {
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
androidComponents.finalizeDsl { extension ->
...
}
}
}
มาดูรายละเอียดของ Callback ที่ใช้ได้และประเภทกรณีการใช้งาน ที่ปลั๊กอินรองรับในแต่ละ Callback กัน
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
}
}
beforeVariants() Callback จะรับ 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() })
}
มีส่วนร่วมในการสร้างแหล่งข้อมูลที่สร้างขึ้น
ปลั๊กอินของคุณสามารถมีส่วนร่วมกับแหล่งที่มาที่สร้างขึ้นได้ 2-3 ประเภท เช่น
- โค้ดของแอปพลิเคชันในไดเรกทอรี
java - ทรัพยากร Android ในไดเรกทอรี
res - แหล่งข้อมูล Java
ในไดเรกทอรี
resources - ชิ้นงาน Android ในไดเรกทอรี
assets
ดูรายการแหล่งที่มาทั้งหมดที่คุณเพิ่มได้ที่ 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.
...
}
}
ดูรายละเอียดเพิ่มเติมได้ที่สูตรการเพิ่มชิ้นงานที่กำหนดเอง
เข้าถึงและแก้ไขอาร์ติแฟกต์
นอกจากจะช่วยให้คุณแก้ไขพร็อพเพอร์ตี้อย่างง่ายในออบเจ็กต์ Variant แล้ว AGP
ยังมีกลไกส่วนขยายที่ช่วยให้คุณอ่านหรือเปลี่ยนรูปแบบ
อาร์ติแฟกต์ขั้นกลางและขั้นสุดท้ายที่สร้างขึ้นระหว่างการบิลด์ได้ด้วย เช่น คุณสามารถ
อ่านเนื้อหาของไฟล์ AndroidManifest.xml ที่ผสานแล้วใน Task ที่กำหนดเองเพื่อ
วิเคราะห์ หรือจะแทนที่เนื้อหาทั้งหมดด้วยเนื้อหาของไฟล์ Manifest
ที่สร้างโดย Task ที่กำหนดเองก็ได้
คุณดูรายการอาร์ติแฟกต์ที่รองรับในปัจจุบันได้ในเอกสารประกอบอ้างอิง
สำหรับคลาส Artifact อาร์ติแฟกต์ทุกประเภทมีคุณสมบัติบางอย่างที่ควรรู้ ดังนี้
Cardinality
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 ที่มีอาร์ติแฟกต์เวอร์ชันสุดท้าย
(หลังจากที่การดำเนินการทั้งหมดในอาร์ติแฟกต์เสร็จสิ้น)
ปลั๊กอินหลายรายการสามารถเพิ่มการดำเนินการจำนวนเท่าใดก็ได้ในอาร์ติแฟกต์ลงในไปป์ไลน์
จากonVariants()การเรียกกลับ และ AGP จะตรวจสอบว่ามีการเชื่อมโยงอย่างถูกต้องเพื่อให้
งานทั้งหมดทำงานในเวลาที่เหมาะสม และมีการสร้างและ
อัปเดตอาร์ติแฟกต์อย่างถูกต้อง ซึ่งหมายความว่าเมื่อการดำเนินการเปลี่ยนเอาต์พุตโดยการต่อท้าย
แทนที่ หรือแปลง การดำเนินการถัดไปจะเห็นอัปเดตเวอร์ชัน
ของอาร์ติแฟกต์เหล่านี้เป็นอินพุต และอื่นๆ
จุดแรกเข้าในการลงทะเบียนการดำเนินการคือคลาส Artifacts
ข้อมูลโค้ดต่อไปนี้แสดงวิธีรับสิทธิ์เข้าถึงอินสแตนซ์ของ Artifacts จากพร็อพเพอร์ตี้ในออบเจ็กต์ Variant ในการเรียกกลับ 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
- การหลีกเลี่ยงการกำหนดค่างาน