ข่าวสารผลิตภัณฑ์

กำหนดค่าและแก้ปัญหาเกี่ยวกับกฎการเก็บรักษาของ R8

ใช้เวลาอ่าน 7 นาที
Ajesh Pai & Ben Weiss

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

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

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

 

 

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

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

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

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

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

ที่ใดที่เขียนกฎของ Keep ได้

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

release {

    isShrinkResources = true

    isMinifyEnabled = true

    proguardFiles(

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

        "proguard-rules.pro",

    )

}

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

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

progaurd.png

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

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

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

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

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

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

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

ตัวเลือกส่วนกลาง

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

การใช้ -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 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 ในทั้งคลาส คุณอาจเรียกคืนการเพิ่มประสิทธิภาพบางอย่างได้โดยอนุญาตการลดขนาดและการปรับให้ยากต่อการอ่าน (Obfuscation)

# 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

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

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

แนวทางปฏิบัติแนะนำสำหรับห้องสมุด

แอป 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 ที่ดีที่สุดคือไม่จำเป็นต้องเขียนกฎ Keep เลย สำหรับแอปจำนวนมาก คุณสามารถทำได้โดยเลือกใช้ไลบรารีที่ทันสมัยซึ่งเน้นการสร้างโค้ดมากกว่าการสะท้อนการสร้างโค้ดช่วยให้เครื่องมือเพิ่มประสิทธิภาพระบุได้ง่ายขึ้นว่าโค้ดใดที่ใช้จริงในขณะรันไทม์และโค้ดใดที่นำออกได้ นอกจากนี้ การไม่ใช้การสะท้อนแบบไดนามิกยังหมายความว่าไม่มีจุดแรกเข้าที่ "ซ่อนอยู่" และด้วยเหตุนี้จึงไม่จำเป็นต้องมีกฎการเก็บรักษา เมื่อเลือกไลบรารีใหม่ ให้เลือกโซลูชันที่ใช้การสร้างโค้ดมากกว่าการสะท้อนเสมอ

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

การแก้ไขข้อบกพร่องและการแก้ปัญหาการกำหนดค่า 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) กลับไปยังไลบรารีที่เฉพาะเจาะจงซึ่งมีกฎนั้น

Ask R8: Why are you keeping this?

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

เขียนโดย

อ่านต่อ