ข่าวสารเกี่ยวกับผลิตภัณฑ์

กำหนดค่าและแก้ปัญหากฎการเก็บรักษา R8

อ่าน 7 นาที
ดูโปรไฟล์ของ Ajesh Pai ดูโปรไฟล์ของ Ben Weiss
Ajesh Pai & Ben Weiss

ในการพัฒนา Android สมัยใหม่ การจัดส่งแอปพลิเคชันขนาดเล็ก รวดเร็ว และปลอดภัยเป็นความคาดหวังพื้นฐานของผู้ใช้ เครื่องมือหลักของระบบบิลด์ Android ที่ใช้ในการดำเนินการนี้คือเครื่องมือเพิ่มประสิทธิภาพ R8  ซึ่งเป็นคอมไพเลอร์ที่จัดการโค้ดที่ไม่ได้ใช้งานและนำทรัพยากรออกเพื่อการลดขนาด การเปลี่ยนชื่อโค้ดหรือการลดขนาด และการเพิ่มประสิทธิภาพแอป

การเปิดใช้ R8 เป็นขั้นตอนสำคัญในการเตรียมแอปสำหรับการเผยแพร่ แต่ต้องให้นักพัฒนาแอปให้คำแนะนำในรูปแบบของ "กฎการเก็บ"

หลังจากอ่านบทความนี้แล้ว โปรดดูวิดีโอ Performance Spotlight Week เกี่ยวกับการเปิดใช้ การแก้ไขข้อบกพร่อง และการแก้ปัญหาเครื่องมือเพิ่มประสิทธิภาพ R8 บน YouTube

 

 

เหตุผลที่ต้องมีกฎการเก็บรักษา

ความจำเป็นในการเขียนกฎ Keep เกิดจากความขัดแย้งหลักที่ว่า R8 เป็นเครื่องมือวิเคราะห์แบบคงที่ แต่แอป Android มักอาศัยรูปแบบการดำเนินการแบบไดนามิก เช่น การสะท้อนหรือการเรียกเข้าและออกจากโค้ดเนทีฟโดยใช้ JNI (Java Native Interface)

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

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

ดูรายละเอียดเพิ่มเติมเกี่ยวกับกฎของ Keep ได้ในคู่มืออย่างเป็นทางการ

ตำแหน่งที่จะเขียนกฎของ Keep

กฎการเก็บรักษาที่กำหนดเองสำหรับแอปพลิเคชันจะเขียนในไฟล์ข้อความ ตามธรรมเนียมแล้ว ไฟล์นี้จะมีชื่อว่า proguard-rules.pro และอยู่ในรูทของแอปหรือโมดูลไลบรารี จากนั้นระบุไฟล์นี้ในไฟล์ build.gradle.kts ของโมดูลในประเภทบิลด์ release

release {

    isShrinkResources = true

    isMinifyEnabled = true

    proguardFiles(

        getDefaultProguardFile("proguard-android-optimize.txt"),

        "proguard-rules.pro",

    )

}

ใช้ไฟล์เริ่มต้นที่ถูกต้อง

เมธอด getDefaultProguardFile จะนำเข้าชุดกฎเริ่มต้นที่ Android SDK จัดเตรียมไว้ให้ การใช้ไฟล์ที่ไม่ถูกต้องอาจทำให้แอปไม่ได้รับการเพิ่มประสิทธิภาพ อย่าลืมใช้ proguard-android-optimize.txt ไฟล์นี้มีกฎการเก็บเริ่มต้นสำหรับคอมโพเนนต์ Android มาตรฐาน และ เปิดใช้การเพิ่มประสิทธิภาพโค้ดของ R8 proguard-android.txtที่ล้าสมัยจะระบุเฉพาะกฎการเก็บ แต่ไม่ได้เปิดใช้การเพิ่มประสิทธิภาพของ R8

progaurd.png

เนื่องจากปัญหานี้ส่งผลต่อประสิทธิภาพอย่างร้ายแรง เราจึงเริ่มเตือนนักพัฒนาแอปเกี่ยวกับการใช้ไฟล์ที่ไม่ถูกต้องตั้งแต่การอัปเดตฟีเจอร์ใหม่ของ Android Studio Narwhal 3 และตั้งแต่ปลั๊กอิน Android Gradle เวอร์ชัน 9.0 เป็นต้นไป เราจะไม่รองรับไฟล์ proguard-android.txt ที่ล้าสมัยอีกต่อไป ดังนั้นโปรดอัปเกรดเป็นเวอร์ชันที่เพิ่มประสิทธิภาพแล้ว

วิธีเขียนกฎของ Keep

กฎการเก็บรักษาประกอบด้วย 3 ส่วนหลัก ได้แก่

  1. ตัวเลือก  เช่น -keep หรือ -keepclassmembers
  2. ตัวแก้ไขที่ไม่บังคับ เช่น allowshrinking
  3. ข้อกำหนดของคลาสที่กำหนดโค้ดที่จะจับคู่

ดูไวยากรณ์และตัวอย่างทั้งหมดได้ในคำแนะนำในการเพิ่มกฎการเก็บ

รูปแบบการต่อต้านกฎการเก็บรักษา

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

ตัวเลือกทั่วโลก

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

การใช้ -dontotptimize จะปิดใช้การเพิ่มประสิทธิภาพของ R8 อย่างมีประสิทธิภาพ ซึ่งส่งผลให้แอปทำงานช้าลง

เมื่อใช้ -dontobfuscate คุณจะปิดใช้การเปลี่ยนชื่อทั้งหมด และเมื่อใช้ -dontshrink จะเป็นการปิดการนำโค้ดที่ไม่ได้ใช้ออก กฎส่วนกลางทั้ง 2 ข้อนี้จะเพิ่มขนาดแอป

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

กฎการเก็บที่กว้างเกินไป

วิธีที่ง่ายที่สุดในการทำให้ประโยชน์ของ R8 เป็นโมฆะคือการเขียนกฎการเก็บรักษาที่กว้างเกินไป กฎเช่นกฎด้านล่างจะสั่งให้เครื่องมือเพิ่มประสิทธิภาพ R8 ไม่ย่อ ไม่ทำให้สับสน และไม่เพิ่มประสิทธิภาพคลาสใดๆ ในแพ็กเกจนี้หรือแพ็กเกจย่อยใดๆ ซึ่งจะนำประโยชน์ของ R8 ออกจากแพ็กเกจนั้นทั้งหมด ลองเขียนกฎของ Keep ที่เจาะจงและแคบลงแทน
 

-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMS

โอเปอเรเตอร์การกลับค่า (!)

ตัวดำเนินการผกผัน (!) ดูเหมือนจะเป็นวิธีที่มีประสิทธิภาพในการยกเว้นแพ็กเกจจากกฎ แต่เรื่องนี้ไม่ได้ง่ายอย่างที่คิด ลองดูตัวอย่างนี้

-keep class !com.example.my_package.** { *; } // USE WITH CAUTION

คุณอาจคิดว่ากฎนี้หมายความว่า "อย่าเก็บคลาสไว้ในcom.example.package" แต่จริงๆ แล้วหมายความว่า "เก็บทุกคลาส เมธอด และพร็อพเพอร์ตี้ ในทั้งแอปพลิเคชันที่ไม่ได้อยู่ใน com.example.package" หากคุณรู้สึกประหลาดใจกับเรื่องนี้ โปรดตรวจสอบการปฏิเสธในการกำหนดค่า R8

กฎที่ซ้ำซ้อนสำหรับคอมโพเนนต์ Android

ข้อผิดพลาดที่พบบ่อยอีกอย่างคือการเพิ่มกฎ Keep สำหรับ Activities, Services หรือ BroadcastReceivers ของแอปด้วยตนเอง ซึ่งไม่จำเป็น proguard-android-optimize.txtไฟล์เริ่มต้นมีกฎที่เกี่ยวข้องสำหรับคอมโพเนนต์ Android มาตรฐานเหล่านี้อยู่แล้วเพื่อให้ทำงานได้ทันที

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

แนวทางปฏิบัติแนะนำในการใช้กฎ

เมื่อทราบแล้วว่าไม่ควรทำอะไร เรามาดูแนวทางปฏิบัติแนะนำกัน

เขียนกฎการเก็บรักษาที่เฉพาะเจาะจง

กฎการเก็บรักษาที่ดีควรแคบและเฉพาะเจาะจงมากที่สุด โดยควรเก็บเฉพาะสิ่งที่จำเป็นเท่านั้น เพื่อให้ R8 เพิ่มประสิทธิภาพส่วนอื่นๆ ได้
 

กฎคุณภาพ

 

-keep class com.example.** { ; }

 

ต่ำ: เก็บแพ็กเกจทั้งหมดและแพ็กเกจย่อย

 

-keep class com.example.MyClass { ; }

 

ต่ำ: เก็บทั้งชั้นเรียน ซึ่งอาจยังกว้างเกินไป
-keepclassmembers class com.example.MyClass {

    private java.lang.String secretMessage;

    public void onNativeEvent(java.lang.String);

}
สูง: ระบบจะเก็บเฉพาะเมธอดและพร็อพเพอร์ตี้ที่เกี่ยวข้องจากคลาสที่เฉพาะเจาะจง

ใช้บรรพบุรุษร่วม

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

# Keep all fields of any class that implements SerializableModel

-keepclassmembers class * implements com.example.models.SerializableModel {

    <fields>;

}

ใช้คำอธิบายประกอบเพื่อกำหนดเป้าหมายไปยังหลายชั้นเรียน

สร้างคำอธิบายประกอบที่กำหนดเอง (เช่น @Serialize) แล้วใช้เพื่อ "ติดแท็ก" คลาสที่ต้องเก็บฟิลด์ไว้ นี่เป็นอีกรูปแบบหนึ่งที่สะอาดตา ประกาศ และปรับขนาดได้สูง คุณยังสร้างกฎ Keep สำหรับคำอธิบายประกอบที่มีอยู่แล้วจากเฟรมเวิร์กที่คุณใช้ได้ด้วย

# Keep all fields of any class annotated with @Serialize

-keepclassmembers class * {

    @com.example.annotations.Serialize <fields>;

}

เลือกตัวเลือก Keep ที่เหมาะสม

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

ตัวเลือก Keepสิ่งที่ทำ
-keepป้องกันไม่ให้ระบบนำชั้นเรียนและสมาชิกที่กล่าวถึงในประกาศ ออกหรือเปลี่ยนชื่อ
-keepclassmembersป้องกันไม่ให้มีการนำสมาชิกที่ระบุออกหรือเปลี่ยนชื่อ แต่จะอนุญาตให้นำชั้นเรียนออกได้ แต่เฉพาะในชั้นเรียนที่ไม่ได้ถูกนำออกด้วยวิธีอื่น
-keepclasseswithmembersรวมกัน: เก็บทั้งคลาสและสมาชิกของคลาสเฉพาะในกรณีที่มีสมาชิกที่ระบุทั้งหมด

ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกการเก็บรักษาได้ในเอกสารประกอบสำหรับตัวเลือกการเก็บรักษา

อนุญาตการเพิ่มประสิทธิภาพด้วยตัวปรับ

ตัวแก้ไข เช่น allowshrinking และ allowobfuscation จะผ่อนปรนกฎ -keep ที่กว้างขวาง ทำให้ R8 กลับมามีอำนาจในการเพิ่มประสิทธิภาพอีกครั้ง ตัวอย่างเช่น หากไลบรารีเดิมบังคับให้คุณใช้ -keep ในทั้งคลาส คุณอาจเรียกคืนการเพิ่มประสิทธิภาพบางอย่างได้โดยอนุญาตการลดขนาดและการปกปิดโค้ด

# Keep this class, but allow R8 to remove it if it's unused and allow R8 to rename it.

-keep,allowshrinking,allowobfuscation class com.example.LegacyClass

เพิ่มตัวเลือกส่วนกลางเพื่อการเพิ่มประสิทธิภาพเพิ่มเติม

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

-repackageclasses เป็นตัวเลือกที่มีประสิทธิภาพซึ่งสั่งให้ R8 ย้ายคลาสที่คลุมเครือทั้งหมดไปยังแพ็กเกจเดียว ซึ่งจะช่วยประหยัดพื้นที่ในไฟล์ DEX ได้อย่างมากด้วยการนำสตริงชื่อแพ็กเกจที่ซ้ำกันออก

-allowaccessmodification ช่วยให้ R8 ขยายการเข้าถึงได้ (เช่น private เป็น public) เพื่อเปิดใช้การแทรกอินไลน์ที่ดุดันมากขึ้น ตอนนี้ระบบจะเปิดใช้โดยค่าเริ่มต้นเมื่อใช้ proguard-android-optimize.txt

คำเตือน: ผู้เขียนไลบรารีต้องไม่เพิ่มแฟล็กการเพิ่มประสิทธิภาพส่วนกลางเหล่านี้ลงในกฎของผู้ใช้ เนื่องจากระบบจะบังคับใช้กับทั้งแอป

และเพื่อให้ชัดเจนยิ่งขึ้น ในเวอร์ชัน 9.0 ของปลั๊กอิน Android Gradle เราจะเริ่มไม่สนใจแฟล็กการเพิ่มประสิทธิภาพส่วนกลางจากไลบรารีทั้งหมด 

แนวทางปฏิบัติแนะนำสำหรับไลบรารี

แอป Android ทุกแอปต้องใช้ไลบรารีไม่ทางใดก็ทางหนึ่ง มาพูดถึงแนวทางปฏิบัติแนะนำสำหรับไลบรารีกัน

สำหรับนักพัฒนาไลบรารี

หากไลบรารีใช้การสะท้อนหรือ JNI คุณมีหน้าที่ต้องระบุกฎการเก็บรักษาที่จำเป็นแก่ผู้ใช้ กฎเหล่านี้จะอยู่ในไฟล์ consumer-rules.pro ซึ่งจะรวมอยู่ในไฟล์ AAR ของไลบรารีโดยอัตโนมัติ

android {

    defaultConfig {

        consumerProguardFiles("consumer-rules.pro")

    }

    ...

}

สำหรับผู้ใช้ไลบรารี

กรองกฎ Keep ที่มีปัญหาออก

หากคุณต้องใช้ไลบรารีที่มีกฎ Keep ที่มีปัญหา คุณสามารถกรองกฎเหล่านั้นในไฟล์ build.gradle.kts ได้โดยเริ่มจาก AGP 9.0 ซึ่งจะบอกให้ R8 ละเว้นกฎที่มาจากทรัพยากร Dependency ที่เฉพาะเจาะจง

release {

    optimization.keepRules {

        // Ignore all consumer rules from this specific library

        it.ignoreFrom("com.somelibrary:somelibrary")

    }

}

กฎการเก็บรักษาที่ดีที่สุดคือไม่มีกฎการเก็บรักษา

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

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีเลือกไลบรารีได้ที่เลือกไลบรารีอย่างชาญฉลาด

การแก้ไขข้อบกพร่องและการแก้ปัญหาการกำหนดค่า R8

เมื่อ R8 นำโค้ดที่ควรเก็บไว้หรือ APK มีขนาดใหญ่กว่าที่คาดไว้ ให้ใช้เครื่องมือเหล่านี้เพื่อวินิจฉัยปัญหา

ค้นหากฎ Keep ที่ซ้ำกันและกฎ Keep ทั่วโลก

เนื่องจาก R8 ผสานกฎจากแหล่งที่มาหลายสิบแห่ง จึงอาจยากที่จะทราบว่าชุดกฎ "สุดท้าย" คืออะไร การเพิ่มแฟล็กนี้ลงในไฟล์ proguard-rules.pro จะสร้างรายงานที่สมบูรณ์ดังนี้

# Outputs the final, merged set of rules to the specified file

-printconfiguration build/outputs/logs/configuration.txt

คุณสามารถค้นหาไฟล์นี้เพื่อหากฎที่ซ้ำซ้อนหรือติดตามกฎที่มีปัญหา (เช่น -dontoptimize) กลับไปยังไลบรารีที่รวมกฎนั้นไว้

ถาม R8: เหตุใดคุณจึงเก็บข้อมูลนี้ไว้

หากชั้นเรียนที่คุณคาดว่าจะถูกนำออกยังคงอยู่ในแอป R8 จะบอกเหตุผลให้คุณทราบ เพียงเพิ่มกฎต่อไปนี้

# Asks R8 to explain why it's keeping a specific class

class com.example.MyUnusedClass

-whyareyoukeeping 

ในระหว่างการสร้าง R8 จะพิมพ์ห่วงโซ่การอ้างอิงที่แน่นอนซึ่งทำให้ต้องเก็บคลาสนั้นไว้ ทำให้คุณสามารถติดตามการอ้างอิงและปรับกฎได้

ดูคำแนะนำแบบเต็มได้ที่ส่วนการแก้ปัญหา R8

ขั้นตอนถัดไป

R8 เป็นเครื่องมือที่มีประสิทธิภาพในการปรับปรุงประสิทธิภาพของแอป Android ประสิทธิภาพของเครื่องมือนี้ขึ้นอยู่กับความเข้าใจที่ถูกต้องเกี่ยวกับการทำงานของเครื่องมือในฐานะเครื่องมือวิเคราะห์แบบคงที่

การเขียนกฎระดับสมาชิกที่เฉพาะเจาะจง การใช้ประโยชน์จากบรรพบุรุษและคำอธิบายประกอบ รวมถึงการเลือกตัวเลือกการเก็บรักษาที่เหมาะสมอย่างรอบคอบจะช่วยให้คุณเก็บรักษาเฉพาะสิ่งที่จำเป็นได้ แนวทางปฏิบัติที่ล้ำสมัยที่สุดคือการไม่จำเป็นต้องใช้กฎเลยโดยเลือกใช้ไลบรารีที่ทันสมัยซึ่งอิงตาม Codegen แทนไลบรารีรุ่นก่อนหน้าที่อิงตามการสะท้อน

ในสัปดาห์ที่เน้นประสิทธิภาพนี้ อย่าลืมดูวิดีโอ Spotlight Week ของวันนี้บน YouTube และร่วมสนุกกับชาเลนจ์ R8 ของเราต่อไป ใช้แฮชแท็ก #optimizationEnabled หากมีคำถามเกี่ยวกับการเปิดใช้หรือการแก้ปัญหา R8 เราพร้อมช่วยเหลือคุณ

ได้เวลาดูสิทธิประโยชน์ด้วยตัวคุณเองแล้ว

เราขอท้าให้คุณเปิดใช้โหมดเต็มรูปแบบของ R8 สำหรับแอปตั้งแต่วันนี้

  1. ทำตามคู่มือนักพัฒนาแอปเพื่อเริ่มต้นใช้งาน เปิดใช้การเพิ่มประสิทธิภาพแอป
  2. ตรวจสอบว่าคุณยังใช้ proguard-android.txt อยู่หรือไม่ แล้วแทนที่ด้วย proguard-android-optimize.txt
  3. จากนั้นวัดผลลัพธ์ อย่าแค่รู้สึกถึงความแตกต่าง แต่ให้ยืนยันความแตกต่างนั้น วัดผลลัพธ์ด้านประสิทธิภาพโดยปรับโค้ดจาก แอปตัวอย่าง Macrobenchmark ใน GitHub เพื่อวัดเวลาเริ่มต้นก่อนและหลัง

เรามั่นใจว่าคุณจะเห็นประสิทธิภาพของแอปดีขึ้นอย่างมีนัยสำคัญ

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

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

เขียนโดย
อ่านต่อ