ในฐานะผู้เขียนไลบรารี คุณต้องดูแลให้นักพัฒนาแอปสามารถรวมไลบรารีของคุณเข้ากับแอปของตนได้อย่างง่ายดาย พร้อมทั้งมอบประสบการณ์การใช้งานคุณภาพสูงแก่ผู้ใช้ปลายทาง ซึ่งหมายความว่าไลบรารีของคุณต้องเข้ากันได้กับการเพิ่มประสิทธิภาพ Android (R8) โดยไม่ต้องมีการตั้งค่าเพิ่มเติมจากนักพัฒนาแอป หรือระบุว่า ไลบรารีอาจไม่เหมาะสมกับการใช้งานใน Android สิ่งสำคัญคือไลบรารีที่ตั้งใจให้ใช้ใน Android ต้องไม่ขัดขวางการเพิ่มประสิทธิภาพแอปที่สำคัญและเป็นไปตามข้อกำหนดการเพิ่มประสิทธิภาพเพิ่มเติม
เอกสารนี้มีไว้สำหรับนักพัฒนาไลบรารีที่เผยแพร่แล้ว แต่อาจเป็นประโยชน์สำหรับนักพัฒนาโมดูลไลบรารีภายในในแอปขนาดใหญ่ที่แยกเป็นโมดูลด้วย
หากคุณเป็นนักพัฒนาแอปและต้องการดูข้อมูลเกี่ยวกับการเพิ่มประสิทธิภาพแอป Android โปรดดูเปิดใช้การเพิ่มประสิทธิภาพแอป ดูข้อมูลเกี่ยวกับไลบรารีที่เหมาะสม ที่จะใช้ได้ที่เลือกไลบรารีอย่างชาญฉลาด
ทำความเข้าใจประเภทกฎการเก็บรักษา
กฎการเก็บมี 2 ประเภทที่แตกต่างกันซึ่งคุณใช้ได้ในคลัง
- กฎการเก็บรักษาของผู้บริโภคต้องระบุกฎที่เก็บรักษาทุกอย่างที่ไลบรารี
แสดง หากไลบรารีใช้การสะท้อนหรือ JNI เพื่อเรียกใช้โค้ดของตัวเอง หรือ
โค้ดที่กำหนดโดยแอปไคลเอ็นต์ กฎเหล่านี้จะต้องอธิบายว่าต้องเก็บโค้ดใดไว้ ไลบรารีควรจัดแพ็กเกจกฎการเก็บรักษาของผู้ใช้ซึ่งใช้รูปแบบเดียวกับกฎการเก็บรักษาแอป กฎเหล่านี้จะรวมอยู่ในอาร์ติแฟกต์ของไลบรารี
(AAR หรือ JAR) และจะใช้โดยอัตโนมัติในระหว่างการเพิ่มประสิทธิภาพแอป Android
เมื่อมีการใช้ไลบรารี กฎเหล่านี้จะได้รับการเก็บไว้ในไฟล์
ที่ระบุด้วยพร็อพเพอร์ตี้
consumerProguardFilesในไฟล์build.gradle.kts(หรือbuild.gradle) ดูข้อมูลเพิ่มเติมได้ที่เขียน กฎการเก็บรักษาสำหรับผู้บริโภค - กฎการเก็บรักษาสำหรับการสร้างคลังจะมีผลเมื่อสร้างคลัง โดยจะจำเป็นก็ต่อเมื่อคุณตัดสินใจที่จะเพิ่มประสิทธิภาพไลบรารีบางส่วนในเวลาสร้างเท่านั้น
โดยจะต้องป้องกันไม่ให้มีการนำ API สาธารณะของไลบรารีออก ไม่เช่นนั้น
API สาธารณะจะไม่ปรากฏในการเผยแพร่ไลบรารี ซึ่งหมายความว่านักพัฒนาแอป
จะใช้ไลบรารีไม่ได้ กฎเหล่านี้จะได้รับการเก็บไว้ในไฟล์
ที่ระบุด้วยพร็อพเพอร์ตี้
proguardFilesในไฟล์build.gradle.kts(หรือbuild.gradle) ดูข้อมูลเพิ่มเติมได้ที่เพิ่มประสิทธิภาพการสร้างไลบรารี AAR
ข้อกำหนดและหลักเกณฑ์ในการเพิ่มประสิทธิภาพ
การกำหนดค่า R8 ในไลบรารีจะส่งผลต่อขนาดไบนารีสุดท้ายและประสิทธิภาพของแอปที่ใช้ นอกเหนือจากแนวทางปฏิบัติแนะนำเกี่ยวกับกฎการเก็บรักษาทั่วไปแล้ว ผู้เขียนไลบรารีต้องปฏิบัติตามข้อกำหนดที่เฉพาะเจาะจงและ พิจารณาหลักเกณฑ์เพิ่มเติม
ปฏิบัติตามข้อกำหนดของการเพิ่มประสิทธิภาพ
ความไม่มีประสิทธิภาพในไลบรารีเป็นสาเหตุหลักที่ทำให้แอปมีขนาดใหญ่เกินไป หน่วยความจำถูกใช้ไปโดยเปล่าประโยชน์ การเริ่มต้นทำงานช้า และเกิดข้อผิดพลาด ANR (ข้อผิดพลาดเกี่ยวกับแอปพลิเคชันไม่ตอบสนอง) ไลบรารีต้อง หลีกเลี่ยงการละเมิดข้อกำหนดต่อไปนี้เพื่อไม่ให้คุณภาพของแอปและประสบการณ์ของผู้ใช้ลดลงอย่างมาก
ไม่มีกฎการเก็บรักษาแบบกว้างหรือระดับแพ็กเกจ: ไลบรารีต้องไม่มีกฎการเก็บรักษาแบบกว้าง ที่เก็บรักษารหัสส่วนใหญ่ในไลบรารีของคุณหรือในไลบรารีอื่น กฎการเก็บรักษาแบบกว้างอาจแก้ปัญหาข้อขัดข้องในระยะสั้นได้ แต่จะทำให้ขนาดแอปของแอปทั้งหมดที่ใช้ไลบรารีของคุณเพิ่มขึ้น
อย่าใส่กฎการเก็บรักษาทั่วทั้งแพ็กเกจ (เช่น
-keep class com.mylibrary.** {*; }) สำหรับแพ็กเกจในคลังหรือคลังอื่นๆ ที่อ้างอิง กฎดังกล่าว จะจำกัดการเพิ่มประสิทธิภาพสำหรับแพ็กเกจเหล่านี้ในแอปทั้งหมดที่ใช้ ไลบรารีของคุณไม่มีกฎส่วนกลางที่ไม่เหมาะสม: ห้ามใช้ตัวเลือกส่วนกลาง เช่น
-dontobfuscateหรือ-allowaccessmodificationใช้ Codegen แทนการสะท้อนเมื่อเป็นไปได้: เมื่อเป็นไปได้ ให้ใช้ การสร้างโค้ด (codegen) แทนการสะท้อน ทั้ง Codegen และ Reflection เป็นแนวทางทั่วไปในการหลีกเลี่ยงโค้ดสำเร็จรูปเมื่อ เขียนโปรแกรม แต่ Codegen จะเข้ากันได้ดีกว่ากับเครื่องมือเพิ่มประสิทธิภาพแอป เช่น R8
เมื่อใช้ Codegen ระบบจะวิเคราะห์และแก้ไขโค้ดในระหว่างกระบวนการบิลด์ เนื่องจากไม่มีการแก้ไขที่สำคัญหลังจากเวลาคอมไพล์ เครื่องมือเพิ่มประสิทธิภาพจึงทราบว่าโค้ดใดที่จำเป็นในท้ายที่สุดและโค้ดใดที่สามารถนำออกได้อย่างปลอดภัย
การสะท้อนจะวิเคราะห์และจัดการโค้ดที่รันไทม์ เนื่องจากโค้ดจะยังไม่เสร็จสมบูรณ์จนกว่าจะมีการดำเนินการจริง ตัวเพิ่มประสิทธิภาพจึงไม่ทราบว่าโค้ดใดที่นำออกได้อย่างปลอดภัย ซึ่งมีแนวโน้มที่จะนำโค้ดที่ใช้ แบบไดนามิกผ่านการสะท้อนในระหว่างรันไทม์ออก ซึ่งจะทำให้แอปขัดข้องสำหรับ ผู้ใช้
ไลบรารีที่ทันสมัยหลายแห่งใช้ Codegen แทนการสะท้อน ดู KSP สำหรับจุดแรกเข้าทั่วไปที่ใช้โดย Room, Dagger2 และ อื่นๆ อีกมากมาย
รองรับโหมดเต็มของ R8: ไลบรารีของคุณไม่ควรขัดข้องเมื่อเปิดใช้โหมดเต็มของ R8 โหมดเต็มของ R8 เป็นโหมดที่แนะนำให้ใช้ R8 และเป็นค่าเริ่มต้นตั้งแต่ AGP 8.0 ซึ่งได้รับการปรับให้เสถียรในปี 2023 หากไลบรารีของคุณขัดข้องภายใต้ R8 วิธีแก้ปัญหาคือการระบุ จุดแรกเข้าของการรีเฟลกชันหรือ JNI ที่เฉพาะเจาะจง แล้วเพิ่มกฎที่กำหนดเป้าหมาย ไม่ใช่การเก็บ ทั้งแพ็กเกจไว้
คำแนะนำเพิ่มเติม
นอกเหนือจากข้อกำหนดในการเพิ่มประสิทธิภาพแล้ว เรายังมีคำแนะนำเพิ่มเติมดังนี้
- อย่าใช้
-repackageclassesในไฟล์กฎการเก็บรักษาของผู้ใช้ของไลบรารี อย่างไรก็ตาม คุณสามารถใช้-repackageclassesที่มีชื่อแพ็กเกจภายใน เช่น<your.library.package>.internalใน ไฟล์กฎการเก็บรักษาบิลด์ของไลบรารีเพื่อเพิ่มประสิทธิภาพบิลด์ของไลบรารี ซึ่งจะช่วยปรับปรุงประสิทธิภาพของไลบรารี ในแอปที่ไม่ได้เพิ่มประสิทธิภาพ อย่างไรก็ตาม โดยทั่วไปแล้วไม่จำเป็นต้องดำเนินการ เนื่องจากควรเพิ่มประสิทธิภาพแอปด้วย - ประกาศแอตทริบิวต์ที่จำเป็นเพื่อให้ไลบรารีทำงานได้ใน
ไฟล์กฎการเก็บรักษาของไลบรารี แม้ว่าอาจมีแอตทริบิวต์ที่ซ้ำซ้อนกับ
แอตทริบิวต์ที่กำหนดไว้ใน
proguard-android-optimize.txtก็ตาม - หากคุณต้องการแอตทริบิวต์ต่อไปนี้ในการเผยแพร่ไลบรารี
ให้เก็บไว้ในไฟล์กฎการเก็บรักษาของบิลด์ของไลบรารีและไม่ใช่ในไฟล์กฎการเก็บรักษาของผู้ใช้ของไลบรารี
AnnotationDefaultEnclosingMethodExceptionsInnerClassesRuntimeInvisibleAnnotationsRuntimeInvisibleParameterAnnotationsRuntimeInvisibleTypeAnnotationsRuntimeVisibleAnnotationsRuntimeVisibleParameterAnnotationsRuntimeVisibleTypeAnnotationsSignature
- ผู้เขียนไลบรารีควรรักษาแอตทริบิวต์
RuntimeVisibleAnnotationsไว้ใน กฎการเก็บรักษาสำหรับผู้ใช้หากใช้คำอธิบายประกอบในรันไทม์ - ผู้เขียนไลบรารีไม่ควรใช้ตัวเลือกส่วนกลางต่อไปนี้ในกฎการเก็บรักษาของผู้ใช้
-include-basedirectory-injars-outjars-libraryjars-repackageclasses-flattenpackagehierarchy-allowaccessmodification-overloadaggressively-renamesourcefileattribute-ignorewarnings-addconfigurationdebugging-printconfiguration-printmapping-printusage-printseeds-applymapping-obfuscationdictionary-classobfuscationdictionary-packageobfuscationdictionary
เมื่อการสะท้อนเป็นเรื่องที่ยอมรับได้
หากจำเป็นต้องใช้การสะท้อน คุณควรสะท้อนเฉพาะในรายการใดรายการหนึ่งต่อไปนี้
- ประเภทเป้าหมายที่เฉพาะเจาะจง (ผู้ใช้ที่เฉพาะเจาะจงหรือคลาสย่อย)
- โค้ดที่ใช้คำอธิบายประกอบรันไทม์ที่เฉพาะเจาะจง
การใช้การสะท้อนในลักษณะนี้จะจำกัดค่าใช้จ่ายรันไทม์ และช่วยให้เขียนกฎการเก็บรักษาที่กำหนดเป้าหมายได้
รูปแบบการสะท้อนที่เฉพาะเจาะจงและตรงเป้าหมายนี้เป็นรูปแบบที่คุณเห็นได้ทั้งในเฟรมเวิร์ก Android (เช่น เมื่อขยายกิจกรรม มุมมอง และ Drawable) และไลบรารี AndroidX (เช่น เมื่อสร้าง WorkManager
ListenableWorkers หรือ RoomDatabases) ในทางตรงกันข้าม การสะท้อนแบบเปิดของ Gson ไม่เหมาะกับการใช้งานในแอป Android
ความเข้าใจผิดที่พบบ่อย
ความเข้าใจผิดที่พบบ่อยบางอย่างอาจทำให้คุณกำหนดค่า R8 ไม่ถูกต้อง ซึ่งรวมถึงรายการต่อไปนี้
ความเข้าใจที่ไม่ถูกต้องเกี่ยวกับการเพิ่มประสิทธิภาพของ R8: การเพิ่มประสิทธิภาพของ R8 ไม่ได้จำกัดอยู่แค่การปรับให้ยากต่อการอ่าน (Obfuscation) เท่านั้น แต่ยังรวมถึงการลดขนาดโค้ดและการเพิ่มประสิทธิภาพเชิงตรรกะด้วยเทคนิคการแทรกเมธอดและการผสานคลาสด้วย ดูข้อมูลเพิ่มเติมได้ที่ภาพรวมการเพิ่มประสิทธิภาพ R8
การข้ามการเพิ่มประสิทธิภาพของไลบรารีที่ทำให้สับสน: ข้อผิดพลาดที่พบบ่อยคือการ ละเว้นไลบรารีจากการเพิ่มประสิทธิภาพ เนื่องจากไลบรารีได้รับการเพิ่มประสิทธิภาพหรือ ทำให้สับสนเมื่อคอมไพล์เป็น AAR (Android Archive) หรือ JAR (Java Archive) การเพิ่มประสิทธิภาพในระหว่างเวลาบิลด์ไลบรารีมีข้อจำกัด และแอปไม่ควรปิดใช้การเพิ่มประสิทธิภาพของไลบรารีโดยรวมไว้ในกฎการเก็บรักษา ดูข้อมูลเพิ่มเติมได้ที่เพิ่มประสิทธิภาพการสร้างไลบรารี AAR
ความเข้าใจที่ไม่ถูกต้องเกี่ยวกับตัวเลือก
-keepกฎ-keepจะป้องกันไม่ให้ R8 เรียกใช้การเพิ่มประสิทธิภาพ ดูข้อมูลเพิ่มเติมได้ที่เลือกตัวเลือกการเก็บที่เหมาะสม
กำหนดค่าการจัดแพ็กเกจกฎ
คุณต้องจัดแพ็กเกจกฎการเก็บรักษาข้อมูลผู้บริโภคอย่างเหมาะสมตามรูปแบบคลังของคุณเพื่อให้ระบบใช้กฎดังกล่าวอย่างถูกต้อง
ไลบรารี AAR
หากต้องการเพิ่มกฎสำหรับผู้ใช้ไลบรารี AAR ให้ใช้ตัวเลือก consumerProguardFiles
ในสคริปต์บิลด์ของโมดูลไลบรารี Android ดูข้อมูลเพิ่มเติมได้ที่คำแนะนำในการสร้างโมดูลไลบรารี
Kotlin
android {
defaultConfig {
consumerProguardFiles("consumer-proguard-rules.pro")
}
...
}
Groovy
android {
defaultConfig {
consumerProguardFiles 'consumer-proguard-rules.pro'
}
...
}
ไลบรารี JAR
หากต้องการรวมกฎกับไลบรารี Kotlin หรือ Java ที่จัดส่งเป็น JAR ให้วางไฟล์กฎในไดเรกทอรี META-INF/proguard/ ของ JAR สุดท้าย โดยใช้ชื่อไฟล์ใดก็ได้
เช่น หากโค้ดของคุณอยู่ใน <libraryroot>/src/main/kotlin ให้วางไฟล์กฎสำหรับผู้บริโภคไว้ที่
<libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro
และระบบจะรวมกฎไว้ในตำแหน่งที่ถูกต้องใน JAR เอาต์พุต
ยืนยันว่ากฎของแพ็กเกจ JAR สุดท้ายถูกต้องโดยตรวจสอบว่ากฎอยู่ในไดเรกทอรี META-INF/proguard
เพิ่มประสิทธิภาพการสร้างไลบรารี AAR (ขั้นสูง)
โดยทั่วไปแล้ว คุณไม่จำเป็นต้องเพิ่มประสิทธิภาพการสร้างไลบรารีโดยตรง เนื่องจาก การเพิ่มประสิทธิภาพที่เป็นไปได้ในเวลาสร้างไลบรารีมีจำกัดมาก ในฐานะนักพัฒนาซอฟต์แวร์ไลบรารี คุณต้องพิจารณาการเพิ่มประสิทธิภาพหลายขั้นตอนและคงลักษณะการทำงานไว้ทั้งในเวลาสร้างไลบรารีและแอปก่อนที่จะเพิ่มประสิทธิภาพไลบรารีนั้น
หากยังต้องการเพิ่มประสิทธิภาพไลบรารีในเวลาบิลด์ Android Gradle Plugin จะรองรับการดำเนินการนี้
Kotlin
android {
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
configureEach {
consumerProguardFiles("consumer-rules.pro")
}
}
}
Groovy
android {
buildTypes {
release {
minifyEnabled true
proguardFiles
getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
configureEach {
consumerProguardFiles "consumer-rules.pro"
}
}
}
โปรดทราบว่าลักษณะการทำงานของ proguardFiles แตกต่างจาก
consumerProguardFiles อย่างมาก
proguardFilesจะใช้ในเวลาบิลด์ โดยมักจะใช้ร่วมกับgetDefaultProguardFile("proguard-android-optimize.txt")เพื่อกำหนดว่าควรเก็บส่วนใดของไลบรารีไว้ในระหว่างการบิลด์ไลบรารี อย่างน้อยที่สุด นี่คือ API สาธารณะของคุณconsumerProguardFilesในทางตรงกันข้ามจะรวมอยู่ในไลบรารีเพื่อส่งผลต่อ การเพิ่มประสิทธิภาพที่จะเกิดขึ้นในภายหลัง ระหว่างการสร้างแอปที่ใช้ ไลบรารีของคุณ
ตัวอย่างเช่น หากไลบรารีใช้การสะท้อนเพื่อสร้างคลาสภายใน คุณอาจต้องกำหนดกฎการเก็บทั้งใน proguardFiles และ consumerProguardFiles
หากคุณใช้ -repackageclasses ในบิลด์ของไลบรารี ให้จัดแพ็กเกจคลาสใหม่เป็นแพ็กเกจย่อย ภายใน แพ็กเกจของไลบรารี เช่น ใช้ -repackageclasses
'com.example.mylibrary.internal' แทน -repackageclasses 'internal'
รองรับ R8 เวอร์ชันต่างๆ (ขั้นสูง)
คุณปรับแต่งกฎเพื่อกำหนดเป้าหมายไปยัง R8 เวอร์ชันที่เฉพาะเจาะจงได้ ซึ่งจะช่วยให้ไลบรารีทำงานได้อย่างเหมาะสมที่สุดในโปรเจ็กต์ที่ใช้ R8 เวอร์ชันใหม่กว่า ขณะเดียวกันก็อนุญาตให้ใช้กฎที่มีอยู่ต่อไปในโปรเจ็กต์ที่ใช้ R8 เวอร์ชันเก่ากว่า
หากต้องการระบุกฎ R8 ที่กำหนดเป้าหมาย คุณต้องรวมกฎเหล่านั้นไว้ในไดเรกทอรี META-INF/com.android.tools ภายใน classes.jar ของ AAR หรือในไดเรกทอรี META-INF/com.android.tools ของ JAR
In an AAR library:
proguard.txt (legacy location, the file name must be "proguard.txt")
classes.jar
└── META-INF
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
In a JAR library:
META-INF
├── proguard/<ProGuard-rule-files> (legacy location)
└── com.android.tools (location of targeted R8 rules)
├── r8-from-<X>-upto-<Y>/<R8-rule-files>
└── ... (more directories with the same name format)
ในไดเรกทอรี META-INF/com.android.tools อาจมีไดเรกทอรีย่อยหลายรายการที่มีชื่อในรูปแบบ r8-from-<X>-upto-<Y> เพื่อระบุว่ากฎเขียนขึ้นสำหรับ R8 เวอร์ชันใด แต่ละไดเรกทอรีย่อยจะมีไฟล์ที่มีกฎ R8 อย่างน้อย 1 ไฟล์ โดยมีชื่อไฟล์และนามสกุลใดก็ได้
โปรดทราบว่าส่วน -from-<X> และ -upto-<Y> เป็นส่วนที่ไม่บังคับ ส่วนเวอร์ชัน <Y>
เป็นเวอร์ชันเฉพาะ และช่วงเวอร์ชันมักจะต่อเนื่องกัน แต่ก็อาจทับซ้อนกันได้เช่นกัน
เช่น r8, r8-upto-8.0.0, r8-from-8.0.0-upto-8.2.0 และ r8-from-8.2.0 คือชื่อไดเรกทอรีที่แสดงถึงชุดกฎ R8 ที่กำหนดเป้าหมาย
กฎในไดเรกทอรี r8 ใช้ได้กับ R8 ทุกเวอร์ชัน R8 สามารถใช้กฎในไดเรกทอรี
r8-from-8.0.0-upto-8.2.0 ได้ตั้งแต่เวอร์ชัน
8.0.0 จนถึงเวอร์ชัน 8.2.0 แต่ไม่รวม
ปลั๊กอิน Android Gradle ใช้ข้อมูลดังกล่าวเพื่อเลือกกฎทั้งหมดที่เวอร์ชัน R8 ปัจจุบันใช้ได้
หากไลบรารีไม่ได้ระบุกฎ R8
เป้าหมาย ปลั๊กอิน Android Gradle จะเลือกกฎจากตำแหน่งเดิม
(proguard.txt สำหรับ AAR หรือ META-INF/proguard/<ProGuard-rule-files> สำหรับ
JAR)