เวลาสร้างนานๆ จะทำให้ขั้นตอนการพัฒนาช้าลง หน้านี้นำเสนอเทคนิคบางอย่างที่สามารถช่วยคุณได้ แก้ปัญหาคอขวดของความเร็วบิลด์
ขั้นตอนทั่วไปในการปรับปรุงความเร็วในการสร้างแอปมีดังนี้
- เพิ่มประสิทธิภาพการกำหนดค่าบิลด์โดยใช้หัวข้อ ขั้นตอนที่ส่งผลดีต่อโปรเจ็กต์ส่วนใหญ่ของ Android Studio ได้ทันที
- ทำโปรไฟล์บิลด์เพื่อระบุและวินิจฉัยบิลด์ บริเวณคอขวดที่มีความซับซ้อนมากขึ้น ซึ่งอาจเฉพาะกับโครงการหรือเวิร์กสเตชันของคุณ
เมื่อพัฒนาแอป ให้ทำให้ใช้งานได้ในอุปกรณ์ที่ใช้ Android 7.0 (API ระดับ 24) ขึ้นไปหากเป็นไปได้ เวอร์ชันใหม่ของ แพลตฟอร์ม Android ใช้กลไกที่ดียิ่งขึ้นสำหรับการอัปเดตแอปของคุณ เช่น Android Runtime (ART) และการรองรับในตัวสำหรับไฟล์ DEX หลายไฟล์
หมายเหตุ: หลังจากสร้างเสร็จครั้งแรกแล้ว คุณอาจสังเกตเห็นว่า ทั้งในส่วนที่สะอาดตาและที่เพิ่มขึ้น สามารถทำงานได้เร็วขึ้นมากแม้ไม่ได้ใช้ การเพิ่มประสิทธิภาพที่อธิบายในหน้านี้ นั่นเป็นเพราะดีมอน Gradle มี "การอุ่นเครื่อง" ระยะเวลาการเพิ่มประสิทธิภาพ คล้ายกับ JVM อื่น กระบวนการ
เพิ่มประสิทธิภาพการกำหนดค่าบิลด์
ทำตามเคล็ดลับเหล่านี้เพื่อปรับปรุงบิลด์ ของโปรเจ็กต์ Android Studio
อัปเดตเครื่องมือให้เป็นปัจจุบันอยู่เสมอ
เครื่องมือ Android ได้รับการเพิ่มประสิทธิภาพบิลด์และฟีเจอร์ใหม่ๆ ด้วย อัปเดตทุกครั้ง เคล็ดลับในหน้านี้จะถือว่าคุณกําลังใช้เวอร์ชันล่าสุด เวอร์ชัน โปรดติดตามผลเพื่อใช้ประโยชน์จากการเพิ่มประสิทธิภาพล่าสุด ถึงปัจจุบัน:
ใช้ KSP แทน kapt
เครื่องมือประมวลผลคำอธิบายประกอบของ Kotlin (kapt) ทำงานได้ช้ากว่า Kotlin มาก ตัวประมวลผลสัญลักษณ์ (KSP) หากคุณเขียนซอร์สโค้ด Kotlin ที่มีคำอธิบายประกอบและใช้เครื่องมือนั้น ประมวลผลคำอธิบายประกอบ (เช่น ห้อง) ที่รองรับ KSP คุณควรย้ายข้อมูลไปยัง KSP
หลีกเลี่ยงการรวบรวมทรัพยากรที่ไม่จำเป็น
หลีกเลี่ยงการคอมไพล์และแพ็กเกจทรัพยากรที่ไม่ได้ทดสอบ เช่น การแปลภาษาเพิ่มเติมและแหล่งข้อมูลความหนาแน่นของหน้าจอ แต่ให้ระบุเพียงรายการเดียวแทน ทรัพยากรภาษาและความหนาแน่นของหน้าจอสำหรับ "เวอร์ชันที่กำลังพัฒนา" รสชาติ [flavor] ดังที่ปรากฏในตัวอย่างต่อไปนี้
ดึงดูด
android { ... productFlavors { dev { ... // The following configuration limits the "dev" flavor to using // English stringresources and xxhdpi screen-density resources. resourceConfigurations "en", "xxhdpi" } ... } }
Kotlin
android { ... productFlavors { create("dev") { ... // The following configuration limits the "dev" flavor to using // English stringresources and xxhdpi screen-density resources. resourceConfigurations("en", "xxhdpi") } ... } }
ทดลองใช้พอร์ทัลปลั๊กอิน Gradle เป็นครั้งสุดท้าย
ใน Android ปลั๊กอินทั้งหมดจะอยู่ใน google()
และ
ที่เก็บ mavenCentral()
รายการ อย่างไรก็ตาม บิลด์ของคุณอาจ
ซึ่งจำเป็นต้องใช้ปลั๊กอินของบุคคลที่สามที่ได้รับการแก้ไขโดยใช้
gradlePluginPortal()
service.
Gradle ค้นหาที่เก็บตามลำดับที่มีการประกาศ
ดังนั้นประสิทธิภาพของบิลด์จะได้รับการปรับปรุงหากที่เก็บที่ระบุไว้มี
ของปลั๊กอินส่วนใหญ่ ดังนั้น ให้ลองใช้ gradlePluginPortal()
รายการโดยวางรายการล่าสุดในบล็อกที่เก็บใน settings.gradle
ของคุณ
ในกรณีส่วนใหญ่ วิธีนี้จะช่วยลดจำนวนการค้นหาปลั๊กอินที่ซ้ำซ้อนและ
จะช่วยเพิ่มความเร็วในการสร้าง
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีที่ Gradle ไปยังส่วนต่างๆ ของที่เก็บหลายแหล่ง โปรดดู การประกาศที่เก็บหลายรายการ ในเอกสารประกอบของ Gradle
ใช้ค่าการกำหนดค่าบิลด์แบบคงที่กับบิลด์การแก้ไขข้อบกพร่อง
ใช้ค่าคงที่เสมอสำหรับพร็อพเพอร์ตี้ที่อยู่ในไฟล์ Manifest หรือไฟล์ทรัพยากรสำหรับ ประเภทบิลด์การแก้ไขข้อบกพร่อง
การใช้รหัสเวอร์ชันแบบไดนามิก ชื่อเวอร์ชัน ทรัพยากร หรือ ตรรกะบิลด์อื่นๆ ที่เปลี่ยนแปลงไฟล์ Manifest จำเป็นต้องมีบิลด์ของแอป ทุกครั้งที่คุณต้องการทำการเปลี่ยนแปลง แม้ว่าการเปลี่ยนแปลงจริงๆ อาจ มิฉะนั้นจะใช้เพียงการสลับด่วนเท่านั้น หากการกำหนดค่าบิลด์ของคุณจำเป็นต้องใช้ แบบไดนามิก จากนั้นแยกพร็อพเพอร์ตี้เหล่านั้นไปยังตัวแปรบิลด์รุ่นและเก็บ ค่าคงที่สำหรับบิลด์การแก้ไขข้อบกพร่องดังที่แสดงในตัวอย่างต่อไปนี้
... // Use a filter to apply onVariants() to a subset of the variants. onVariants(selector().withBuildType("release")) { variant -> // Because an app module can have multiple outputs when using multi-APK, versionCode // is only available on the variant output. // Gather the output when we are in single mode and there is no multi-APK. val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE } // Create the version code generating task. val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) { it.outputFile.set(project.layout.buildDirectory.file("versionCode${variant.name}.txt")) } // Wire the 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.flatMap { it.outputFile.map { it.asFile.readText().toInt() } }) } ... abstract class VersionCodeTask : DefaultTask() { @get:OutputFile abstract val outputFile: RegularFileProperty @TaskAction fun action() { outputFile.get().asFile.writeText("1.1.1") } }
ดูสูตร setVersionsFromTask บน GitHub เพื่อดูวิธีตั้งค่า รหัสเวอร์ชันแบบไดนามิกในโปรเจ็กต์ของคุณ
ใช้เวอร์ชันที่ขึ้นต่อกันแบบคงที่
เมื่อคุณประกาศทรัพยากร Dependency ในไฟล์ build.gradle
ให้หลีกเลี่ยงการใช้เวอร์ชันแบบไดนามิก
ตัวเลข (ที่มีเครื่องหมายบวกต่อท้าย เช่น 'com.android.tools.build:gradle:2.+'
)
การใช้หมายเลขเวอร์ชันแบบไดนามิกอาจทำให้เกิดการอัปเดตเวอร์ชันที่ไม่คาดคิด ทำให้แปลงเวอร์ชันได้ยาก
และบิลด์ที่ช้ากว่าซึ่งเกิดจากการที่ Gradle ตรวจหาอัปเดต
โปรดใช้หมายเลขเวอร์ชันแบบคงที่แทน
สร้างโมดูลไลบรารี
มองหาโค้ดในแอปที่สามารถแปลงเป็นโมดูลไลบรารี Android ได้ การทำให้โค้ดเป็นโมดูลย่อยด้วยวิธีนี้ทำให้ระบบบิลด์สามารถคอมไพล์เฉพาะ โมดูลที่คุณแก้ไขและแคชเอาต์พุตเหล่านั้นสำหรับบิลด์ในอนาคต การแยกส่วนยังช่วยให้ การดำเนินการโครงการพร้อมกันจะมีประสิทธิภาพมากขึ้นเมื่อคุณ ให้เปิดใช้การเพิ่มประสิทธิภาพนั้น
สร้างงานสำหรับตรรกะบิลด์ที่กำหนดเอง
หลังจากที่คุณสร้างโปรไฟล์บิลด์ หากบิลด์
โปรไฟล์แสดงว่าใช้เวลาสร้างค่อนข้างนานใน **การกำหนดค่า
ระยะของโครงการ** ให้ตรวจสอบสคริปต์ build.gradle
ของคุณและมองหา
ที่จะรวมไว้ในงาน Gradle ที่กำหนดเอง โดยการย้ายตรรกะบิลด์บางส่วน
เป็นงาน คุณจึงช่วยให้มั่นใจว่างานจะทำงานเมื่อจำเป็นต้องใช้เท่านั้น และสามารถแคชผลลัพธ์ได้
บิลด์ที่ตามมา และตรรกะของบิลด์นั้นจะมีสิทธิ์ทำงานพร้อมกันหากคุณเปิดใช้การดำเนินการโปรเจ็กต์พร้อมกัน ดูข้อมูลเพิ่มเติมเกี่ยวกับ tak สำหรับบิลด์ที่กำหนดเอง
ให้อ่านเอกสาร Gradle อย่างเป็นทางการ
เคล็ดลับ: หากบิลด์มีงานที่กำหนดเองจำนวนมาก คุณอาจ
ต้องการจัดระเบียบไฟล์ build.gradle
โดยสร้างคลาสงานที่กำหนดเอง เพิ่มชั้นเรียนของคุณลงใน
project-root/buildSrc/src/main/groovy/
ไดเรกทอรี;
Gradle รวมชั้นเรียนเหล่านั้นไว้ในคลาสพาธสำหรับทุกคนโดยอัตโนมัติ
build.gradle
ไฟล์ในโปรเจ็กต์
แปลงรูปภาพเป็น WebP
WebP คือไฟล์ภาพ ที่มีการบีบอัดแบบสูญเสียบางส่วน (เช่น JPEG) และมีความโปร่งใส (เช่น PNG) WebP สามารถบีบอัดได้ดีกว่า JPEG หรือ PNG
การลดขนาดไฟล์รูปภาพโดยไม่ต้องบีบอัดเวลาบิลด์ สามารถเร่งงานสร้างของคุณได้ โดยเฉพาะอย่างยิ่งหากแอปของคุณใช้อิมเมจจำนวนมาก ที่ไม่ซับซ้อน แต่คุณอาจเห็นว่าการใช้งาน CPU ของอุปกรณ์เพิ่มขึ้นเล็กน้อยในขณะที่ การขยายรูปภาพ WebP ใช้ Android Studio ได้ง่ายๆ แปลงรูปภาพ กับ WebP
ปิดใช้การคลิปไฟล์ PNG
หากไม่แปลง PNG ไปยัง WebP ได้ คุณยังเร่งสร้างให้เร็วขึ้นได้โดยปิดใช้ การบีบอัดรูปภาพทุกครั้งที่คุณสร้างแอป
หากใช้ปลั๊กอิน Android Gradle 3.0.0
หรือสูงกว่า ตามค่าเริ่มต้น ระบบจะปิดใช้งานการครอบตัด PNG สำหรับ "การแก้ไขข้อบกพร่อง" ประเภทบิลด์ หากต้องการปิดใช้
การเพิ่มประสิทธิภาพสำหรับบิลด์ประเภทอื่นๆ ให้เพิ่มค่าต่อไปนี้ลงในไฟล์ build.gradle
ดึงดูด
android { buildTypes { release { // Disables PNG crunching for the "release" build type. crunchPngs false } } }
Kotlin
android { buildTypes { getByName("release") { // Disables PNG crunching for the "release" build type. isCrunchPngs = false } } }
เนื่องจากประเภทบิลด์หรือรสชาติของผลิตภัณฑ์ไม่ได้กำหนดพร็อพเพอร์ตี้นี้ คุณจึงต้อง
เพื่อตั้งค่าพร็อพเพอร์ตี้นี้เป็น true
ด้วยตนเองเมื่อสร้างรุ่น
เวอร์ชันแอปของคุณ
ทดลองใช้เครื่องมือเก็บขยะแบบขนาน JVM
ปรับปรุงประสิทธิภาพของบิลด์ได้ด้วยการกำหนดค่าเครื่องมือรวบรวมขยะ JVM ที่เหมาะสมที่สุดที่ Gradle นำมาใช้ แม้ว่า JDK 8 จะกำหนดค่าให้ใช้เครื่องมือเก็บข้อมูลขยะแบบขนานโดยค่าเริ่มต้น แต่ JDK 9 ขึ้นไป มีการกำหนดค่าให้ใช้ เครื่องมือเก็บขยะ G1
เราขอแนะนำเพื่อเพิ่มโอกาสในการปรับปรุงประสิทธิภาพของบิลด์
การทดสอบบิลด์ Gradle ของคุณกับโหมดคู่ขนาน
เครื่องมือเก็บขยะ ใน gradle.properties
ให้ตั้งค่าต่อไปนี้
org.gradle.jvmargs=-XX:+UseParallelGC
หากมีตัวเลือกอื่นๆ ที่ตั้งค่าไว้แล้วในช่องนี้ ให้เพิ่มตัวเลือกใหม่ดังนี้
org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC
หากต้องการวัดความเร็วของบิลด์ด้วยการกำหนดค่าต่างๆ โปรดดู ทำโปรไฟล์บิลด์
เพิ่มขนาดฮีป JVM
ถ้าคุณสังเกตเห็นงานสร้างที่ช้า และโดยเฉพาะอย่างยิ่ง ที่เก็บข้อมูลขยะใช้เวลามากกว่า 15% ของ
ใน
เครื่องมือวิเคราะห์บิลด์
คุณควรเพิ่มขนาดฮีปของ Java Virtual Machine (JVM)
ในไฟล์ gradle.properties
ให้กำหนดขีดจำกัดเป็น 4, 6 หรือ 8 กิกะไบต์
ดังที่ปรากฏในตัวอย่างต่อไปนี้
org.gradle.jvmargs=-Xmx6g
จากนั้นทดสอบเพื่อเพิ่มความเร็วของบิลด์ วิธีที่ง่ายที่สุดในการระบุฮีปที่เหมาะสมที่สุด ขนาดคือเพิ่มขีดจำกัดขึ้นเล็กน้อย แล้วทดสอบว่าบิลด์เพียงพอ เร็วขึ้น
หากคุณใช้ เครื่องมือเก็บขยะแบบขนาน JVM บรรทัดทั้งบรรทัดควรมีลักษณะดังนี้
org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g
คุณสามารถวิเคราะห์ข้อผิดพลาดด้านหน่วยความจํา JVM ได้โดยการบิด HeapDumpOnOutOfMemoryError แจ้งว่าไม่เหมาะสม การทำเช่นนี้จะทำให้ JVM สร้างฮีปดัมป์เมื่อหน่วยความจำเต็ม
ใช้คลาส R ที่ไม่ใช่แบบสําเนา
ใช้ R
คลาสแบบไม่สับเปลี่ยนเพื่อให้สร้างบิลด์ได้เร็วขึ้น
สำหรับแอปที่มีหลายโมดูล เพราะจะช่วยป้องกันการสร้างทรัพยากรซ้ำโดยตรวจสอบว่า
คลาส R
ของแต่ละโมดูลมีเฉพาะการอ้างอิงไปยังทรัพยากรของตนเองโดยไม่ได้ดึงข้อมูลการอ้างอิงจาก
ทรัพยากร Dependency โดยตรง ซึ่งจะทำให้สร้างบิลด์ได้เร็วขึ้นและได้ประโยชน์ที่สอดคล้องกันจากการคอมไพล์
เพื่อหลีกเลี่ยง ซึ่งเป็นลักษณะการทำงานเริ่มต้นในปลั๊กอิน Android Gradle เวอร์ชัน 8.0.0 ขึ้นไป
ตั้งแต่ Android Studio Bumblebee เป็นต้นไป ระบบจะเปิดใช้ชั้นเรียน R
แบบไม่เปลี่ยนชั่วคราวสำหรับโปรเจ็กต์ใหม่โดยค่าเริ่มต้น
สำหรับโปรเจ็กต์ที่สร้างด้วย Android Studio เวอร์ชันก่อนหน้า ให้อัปเดตโปรเจ็กต์เหล่านั้นไปใช้แบบไม่ใช่แบบสัญจร
R
ชั้นเรียนโดยไปที่เปลี่ยนโครงสร้างภายในโค้ด > ย้ายข้อมูลไปยังคลาส R แบบไม่เปลี่ยนผ่าน
โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับแหล่งข้อมูลของแอปและชั้นเรียน R
ที่
ภาพรวมแหล่งข้อมูลของแอป
ใช้คลาส R ที่ไม่คงที่
ใช้คลาส R
ที่ไม่คงที่
ในแอปและการทดสอบเพื่อปรับปรุงการคอมไพล์ Java
และช่วยให้ลดขนาดทรัพยากรได้แม่นยำยิ่งขึ้น ช่องของชั้นเรียน R
ช่อง
สำหรับไลบรารีต่างๆ เสมอ เนื่องจากทรัพยากรมีตัวเลข
เมื่อทำแพ็กเกจ APK สำหรับแอปหรือการทดสอบที่ต้องใช้ไลบรารีนั้น
ซึ่งเป็นลักษณะการทำงานเริ่มต้นในปลั๊กอิน Android Gradle 8.0.0 ขึ้นไป
ปิดใช้ Flag Jetifier
เนื่องจากโปรเจ็กต์ส่วนใหญ่ใช้ไลบรารี AndroidX โดยตรง คุณจึงนำโค้ด
Flag Jetifier เพื่อประสิทธิภาพในการสร้างที่ดียิ่งขึ้น วิธีนำออก
ธง Jetifier ตั้งค่า android.enableJetifier=false
ใน
gradle.properties
ตัววิเคราะห์บิลด์สามารถทำการตรวจสอบเพื่อดูว่าแฟล็กสามารถ ถูกนำออกอย่างปลอดภัยเพื่อให้โปรเจ็กต์มีประสิทธิภาพในการสร้างที่ดีขึ้นและย้ายข้อมูลออกจาก ไลบรารีการสนับสนุน Android ที่ไม่มีการบำรุงรักษา หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับเครื่องมือวิเคราะห์บิลด์ โปรดดู แก้ปัญหาประสิทธิภาพของบิลด์
ใช้แคชการกำหนดค่า
แคชการกำหนดค่า ช่วยให้ Gradle บันทึกข้อมูลเกี่ยวกับกราฟงานของบิลด์และนำกลับมาใช้ซ้ำในบิลด์ต่อๆ มาได้ Gradle ไม่จำเป็นต้องกำหนดค่าบิลด์ทั้งหมดอีกครั้ง
หากต้องการเปิดใช้งานแคชการกำหนดค่า ให้ทำตามขั้นตอนต่อไปนี้
- ตรวจสอบว่าปลั๊กอินโปรเจ็กต์ทั้งหมดใช้งานร่วมกันได้
ใช้เมนู สร้างเครื่องมือวิเคราะห์เพื่อตรวจสอบว่า โปรเจ็กต์เข้ากันได้กับแคชการกำหนดค่า ตัววิเคราะห์บิลด์จะเรียกใช้ลำดับการทดสอบ เพื่อระบุว่าสามารถเปิดฟีเจอร์นี้สำหรับโปรเจ็กต์ได้หรือไม่ โปรดดู ปัญหา #13490 สำหรับ รายการปลั๊กอินที่สนับสนุน
เพิ่มโค้ดต่อไปนี้ลงในไฟล์
gradle.properties
org.gradle.configuration-cache=true # Use this flag carefully, in case some of the plugins are not fully compatible. org.gradle.configuration-cache.problems=warn
เมื่อเปิดใช้แคชการกำหนดค่า ครั้งแรกที่คุณเรียกใช้โปรเจ็กต์ เอาต์พุตของบิลด์
พูดว่า Calculating task graph as no configuration cache is available for tasks
ระหว่าง
การเรียกใช้ครั้งต่อๆ ไป เอาต์พุตของบิลด์จะระบุว่า Reusing configuration cache
หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับแคชการกำหนดค่า โปรดดูบล็อกโพสต์ เจาะลึกการแคชการกำหนดค่า และ ในเอกสารของ Gradle เกี่ยวกับ แคชการกำหนดค่า
พบปัญหาเกี่ยวกับแคชการกำหนดค่าใน Gradle 8.1 และปลั๊กอิน Android Gradle 8.1
แคชการกำหนดค่ามีความเสถียรใน Gradle 8.1 และได้แนะนำ File API ด้วย
เครื่องมือวัด Conversion การโทร เช่น File.exists()
, File.isDirectory()
และ File.list()
จะบันทึกโดย
Gradle ติดตามไฟล์อินพุตการกำหนดค่า
ปลั๊กอิน Android Gradle (AGP) 8.1 ใช้ API ของ File
เหล่านี้สำหรับบางไฟล์ที่ Gradle ควร
ไม่ถือว่าเป็นอินพุตของแคช การดำเนินการนี้จะทริกเกอร์การทำให้แคชใช้งานไม่ได้เพิ่มเติมเมื่อใช้กับ
Gradle 8.1 ขึ้นไปจะทำให้ประสิทธิภาพบิลด์ช้าลง
ข้อมูลต่อไปนี้ถือเป็นอินพุตแคชใน AGP 8.1
อินพุต | เครื่องมือติดตามปัญหา | แก้ไขแล้วใน |
$GRADLE_USER_HOME/android/FakeDependency.jar | ปัญหา #289232054 | AGP 8.2 |
เอาต์พุต cmake | ปัญหา #287676077 | AGP 8.2 |
$GRADLE_USER_HOME/.android/analytics.settings | ปัญหา #278767328 | AGP 8.3 |
หากคุณใช้ API เหล่านี้หรือปลั๊กอินที่ใช้ API เหล่านี้ คุณอาจพบปัญหาการถดถอยในเวลาบิลด์ เนื่องจากตรรกะบิลด์บางรายการที่ใช้ API เหล่านี้ อาจทริกเกอร์การทำให้แคชใช้งานไม่ได้เพิ่มเติม โปรดดู การปรับปรุงการติดตามอินพุตการกําหนดค่าบิลด์ เพื่อพูดคุยเกี่ยวกับรูปแบบเหล่านี้และวิธีแก้ไขตรรกะของบิลด์ หรือปิดใช้ชั่วคราว การติดตาม API ของไฟล์