หากต้องการให้แอปมีขนาดเล็กและรวดเร็วที่สุด คุณควรเพิ่มประสิทธิภาพและลดขนาด
บิลด์ที่เผยแพร่กับ isMinifyEnabled = true
การทำเช่นนี้จะทำให้มีการย่อ ซึ่งจะนำโค้ดและทรัพยากรที่ไม่ได้ใช้งานออก การสร้างความสับสน ซึ่งจะย่อชื่อชั้นเรียนและสมาชิกของแอปให้สั้นลง และ การเพิ่มประสิทธิภาพ ซึ่งใช้กลยุทธ์เชิงรุกมากขึ้นเพื่อลด และเพิ่มประสิทธิภาพของแอป หน้านี้อธิบายวิธีที่ R8 ทำงานเวลาคอมไพล์เหล่านี้สำหรับโปรเจ็กต์ของคุณ และวิธีที่คุณสามารถปรับแต่ง ให้พวกเขา
เมื่อสร้างโปรเจ็กต์โดยใช้ ปลั๊กอิน Android Gradle 3.4.0 ขึ้นไป ปลั๊กอินจะไม่ใช้ ProGuard ในการเพิ่มประสิทธิภาพโค้ดเวลาคอมไพล์อีกต่อไป แต่ปลั๊กอินจะทำงานร่วมกับคอมไพเลอร์ R8 เพื่อจัดการสิ่งต่างๆ ต่อไปนี้ งานเวลาคอมไพล์:
- การย่อโค้ด (หรือการสั่นสะเทือน): ตรวจจับและนำโค้ดที่ไม่ได้ใช้งานออกอย่างปลอดภัย ชั้นเรียน ฟิลด์ วิธีการ และแอตทริบิวต์จากแอปและคลัง ทรัพยากร Dependency (ซึ่งทำให้เครื่องมือนี้มีประโยชน์ต่อการจัดการ ขีดจำกัดการอ้างอิง 64,000 รายการ) ตัวอย่างเช่น หากคุณใช้ API ของทรัพยากร Dependency ของไลบรารีเพียงไม่กี่รายการ การย่อขนาดก็สามารถระบุโค้ดไลบรารีได้ ที่แอปของคุณไม่ได้ใช้ และนำเฉพาะโค้ดนั้นออกจากแอป ถึง ดูข้อมูลเพิ่มเติม ไปที่ส่วนเกี่ยวกับวิธีลดขนาดโค้ด
- การลดขนาดทรัพยากร: นำทรัพยากรที่ไม่ได้ใช้ออกจากแอปแพ็กเกจของคุณ รวมถึงทรัพยากรที่ไม่ได้ใช้ในทรัพยากร Dependency ของไลบรารีของแอป ทำงานได้ใน พร้อมกับการลดขนาดโค้ด ซึ่งเมื่อนำโค้ดที่ไม่ได้ใช้งานออกไปแล้ว ทรัพยากรที่ไม่มีการอ้างอิงอีกต่อไปก็ถูกนำออกได้อย่างปลอดภัยเช่นกัน เพื่อเรียนรู้ เพิ่มเติม ให้ไปที่ส่วนเกี่ยวกับวิธี ลดขนาดทรัพยากร
- การเพิ่มประสิทธิภาพ: ตรวจสอบและเขียนโค้ดใหม่เพื่อปรับปรุงรันไทม์
ที่มีประสิทธิภาพมากขึ้นและลดขนาดไฟล์ DEX ของแอปได้มากขึ้น ช่วงเวลานี้
ปรับปรุงประสิทธิภาพขณะรันไทม์ของโค้ดได้ถึง 30%
การเริ่มต้นและการจับเวลาเฟรม เช่น หาก R8 ตรวจพบว่า
else {}
Branch ของคำสั่ง if/else ที่ไม่มีการใช้ R8 จะนำโค้ดสำหรับ สาขาelse {}
ดูข้อมูลเพิ่มเติมได้ในหัวข้อ การเพิ่มประสิทธิภาพโค้ด - การสร้างความสับสน (หรือการลดตัวระบุ): ย่อชื่อคลาส และสมาชิก ซึ่งทำให้ไฟล์ DEX มีขนาดเล็กลง ดูข้อมูลเพิ่มเติมได้ที่ ซึ่งเกี่ยวกับวิธีปรับโค้ดให้ยากต่อการอ่าน (Obfuscate)
เมื่อสร้างแอปเวอร์ชันที่เผยแพร่ คุณสามารถกำหนดค่า R8 ให้ทำงาน งานเวลาคอมไพล์ที่อธิบายไว้ข้างต้นให้คุณ นอกจากนี้ คุณยังสามารถปิดใช้ หรือปรับแต่งลักษณะการทำงานของ R8 ผ่านไฟล์กฎ ProGuard อันที่จริง R8 ทำงานกับไฟล์กฎ ProGuard ที่มีอยู่ทั้งหมดได้ ดังนั้น คุณไม่ควรต้องเปลี่ยนแปลงการอัปเดตปลั๊กอิน Android Gradle เพื่อใช้ R8 กฎที่มีอยู่
เปิดใช้การย่อ การสร้างความสับสน และการเพิ่มประสิทธิภาพ
เมื่อคุณใช้ Android Studio 3.4 หรือปลั๊กอิน Android Gradle 3.4.0 ขึ้นไป R8 จะ คอมไพเลอร์เริ่มต้นที่แปลง Java Bytecode ของโปรเจ็กต์เป็น DEX ที่ทำงานบนแพลตฟอร์ม Android อย่างไรก็ตาม เมื่อคุณสร้างโปรเจ็กต์ใหม่ การใช้ Android Studio การย่อขนาด การสร้างความสับสน และการเพิ่มประสิทธิภาพโค้ดไม่ได้ เปิดใช้งานโดยค่าเริ่มต้น นั่นเป็นเพราะการเพิ่มประสิทธิภาพเวลาคอมไพล์เหล่านี้จะเพิ่ม สร้างเวลาสำหรับโครงการของคุณ และอาจสร้างข้อบกพร่องหากคุณไม่ ปรับแต่งโค้ดที่จะเก็บไว้
ดังนั้น วิธีที่ดีที่สุดคือการเปิดใช้งานเวลาคอมไพล์เหล่านี้เมื่อสร้างเวอร์ชันสุดท้าย ของแอป โดยทดสอบก่อนที่จะเผยแพร่ หากต้องการเปิดใช้การย่อ การสร้างความสับสน และการเพิ่มประสิทธิภาพ รวมถึงสิ่งต่อไปนี้ในสคริปต์บิลด์ระดับโปรเจ็กต์
Kotlin
android { buildTypes { getByName("release") { // Enables code shrinking, obfuscation, and optimization for only // your project's release build type. Make sure to use a build // variant with `isDebuggable=false`. isMinifyEnabled = true // Enables resource shrinking, which is performed by the // Android Gradle plugin. isShrinkResources = true proguardFiles( // Includes the default ProGuard rules files that are packaged with // the Android Gradle plugin. To learn more, go to the section about // R8 configuration files. getDefaultProguardFile("proguard-android-optimize.txt"), // Includes a local, custom Proguard rules file "proguard-rules.pro" ) } } ... }
ดึงดูด
android { buildTypes { release { // Enables code shrinking, obfuscation, and optimization for only // your project's release build type. Make sure to use a build // variant with `debuggable false`. minifyEnabled true // Enables resource shrinking, which is performed by the // Android Gradle plugin. shrinkResources true // Includes the default ProGuard rules files that are packaged with // the Android Gradle plugin. To learn more, go to the section about // R8 configuration files. proguardFiles getDefaultProguardFile( 'proguard-android-optimize.txt'), 'proguard-rules.pro' } } ... }
ไฟล์การกำหนดค่า R8
R8 ใช้ไฟล์กฎ ProGuard เพื่อแก้ไขลักษณะการทำงานเริ่มต้นและเพิ่มประสิทธิภาพ เข้าใจโครงสร้างของแอป เช่น ชั้นเรียนที่เป็นจุดแรกเข้า ลงในโค้ดของแอป แม้ว่าคุณจะสามารถแก้ไขไฟล์กฎเหล่านี้ได้บางไฟล์ สามารถสร้างกฎขึ้นโดยอัตโนมัติโดยเครื่องมือเวลาคอมไพล์ เช่น AAPT2 หรือ ได้รับมาจากทรัพยากร Dependency ของไลบรารีของแอป ตารางด้านล่างจะอธิบาย แหล่งที่มาของไฟล์กฎ ProGuard ที่ R8 ใช้
แหล่งที่มา | ตำแหน่ง | คำอธิบาย |
Android Studio | <module-dir>/proguard-rules.pro
|
เมื่อคุณสร้างโมดูลใหม่โดยใช้ Android Studio IDE จะสร้าง
proguard-rules.pro ในไดเรกทอรีรากของโมดูลนั้น
โดยค่าเริ่มต้น ไฟล์นี้จะไม่ใช้กฎใดๆ ใส่ข้อมูลของคุณเอง ที่นี่มีกฎ ProGuard เช่น กำหนดเอง เก็บกฎ |
ปลั๊กอิน Android Gradle | สร้างโดยปลั๊กอิน Android Gradle ในเวลาคอมไพล์ | ปลั๊กอิน Android Gradle สร้างขึ้น
proguard-android-optimize.txt ซึ่งมีกฎที่
มีประโยชน์กับโครงการ Android ส่วนใหญ่ และทำให้
@Keep*
คำอธิบายประกอบ
โดยค่าเริ่มต้น เมื่อสร้างโมดูลใหม่โดยใช้ Android Studio ระดับโมดูล สคริปต์บิลด์มีไฟล์กฎนี้ในบิลด์ที่เผยแพร่ สำหรับคุณ
หมายเหตุ: ปลั๊กอิน Android Gradle มี ProGuard ที่กำหนดล่วงหน้าเพิ่มเติม
แต่ขอแนะนำให้ใช้
|
ทรัพยากร Dependency ของไลบรารี |
ในคลัง AAR:
ในไลบรารี JAR: นอกเหนือจากประเทศเหล่านี้แล้ว ปลั๊กอิน Android Gradle 3.6 ขึ้นไป รองรับกฎการย่อเป้าหมาย |
หากมีการเผยแพร่ไลบรารี AAR หรือ JAR ด้วยไฟล์กฎของตนเอง และคุณ รวมไลบรารีนั้นเป็นทรัพยากร Dependency เวลาคอมไพล์ R8 โดยอัตโนมัติ ใช้กฎเหล่านั้นเมื่อคอมไพล์โปรเจ็กต์ นอกเหนือจากกฎ ProGuard ทั่วไปแล้ว ปลั๊กอิน Android Gradle 3.6 ขึ้นไปรองรับ กฎการย่อเป้าหมาย นี่คือกฎ ซึ่งกำหนดเป้าหมายไปยังนักย่อขนาดที่เฉพาะเจาะจง (R8 หรือ ProGuard) รวมทั้ง ที่เล็กลง การใช้ไฟล์กฎที่รวมอยู่ในไลบรารีจะมีประโยชน์หากมี ต้องมีกฎเพื่อให้ไลบรารีทำงานได้อย่างถูกต้อง กล่าวคือ ได้ทำตามขั้นตอนการแก้ปัญหาให้คุณแล้ว อย่างไรก็ตาม คุณควรทราบว่า เนื่องจากกฎเป็นสิ่งที่เพิ่มเข้ามา กฎบางอย่างที่ขึ้นอยู่กับไลบรารีจะไม่สามารถนำออกและ อาจส่งผลต่อการรวบรวมส่วนอื่นๆ ของแอป ตัวอย่างเช่น หาก ไลบรารีมีกฎสำหรับปิดใช้การเพิ่มประสิทธิภาพโค้ด กฎนั้นปิดใช้ การเพิ่มประสิทธิภาพสำหรับทั้งโปรเจ็กต์ |
เครื่องมือแพ็กเกจเนื้อหา Android 2 (AAPT2) | หลังจากสร้างโปรเจ็กต์ด้วย minifyEnabled true แล้ว ให้ทำดังนี้
วันที่ <module-dir>/build/intermediates/aapt_proguard_file/.../aapt_rules.txt
|
AAPT2 สร้างกฎ Keep โดยอิงตามการอ้างอิงไปยังคลาสในแอป ไฟล์ Manifest, เลย์เอาต์ และทรัพยากรอื่นๆ ของแอป เช่น AAPT2 มี เก็บกฎสำหรับแต่ละกิจกรรมที่คุณลงทะเบียนในไฟล์ Manifest ของแอปเป็น จุดแรกเข้า |
ไฟล์การกำหนดค่าที่กำหนดเอง | โดยค่าเริ่มต้น เมื่อคุณสร้างโมดูลใหม่โดยใช้ Android Studio ไฟล์ IDE
สร้าง <module-dir>/proguard-rules.pro เพื่อให้คุณเพิ่มของคุณเอง
กฎ
|
คุณสามารถรวมการกำหนดค่าเพิ่มเติม และ R8 จะนำไปประมวลผลในเวลาคอมไพล์ |
เมื่อคุณตั้งค่าพร็อพเพอร์ตี้ minifyEnabled
เป็น true
R8 จะรวมกฎจาก
แหล่งข้อมูลที่มีอยู่ตามรายการด้านบน อย่าลืมคำนึงถึงเรื่องต่อไปนี้
แก้ปัญหาด้วย R8 เพราะทรัพยากร Dependency ของเวลาคอมไพล์อื่นๆ
เช่น ทรัพยากร Dependency ของไลบรารี อาจทำให้
ลักษณะการทำงานของ R8 เปลี่ยนไป
ไม่รู้จัก
เพื่อแสดงรายงานฉบับเต็มของกฎทั้งหมดที่ R8 ใช้เมื่อสร้าง
ให้ใส่ข้อมูลต่อไปนี้ในไฟล์ proguard-rules.pro
ของโมดูล
// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt
กฎการย่อเป้าหมาย
ปลั๊กอิน Android Gradle 3.6 ขึ้นไปรองรับไลบรารี กฎที่กำหนดเป้าหมาย อุปกรณ์ย่อขนาด (R8 หรือ ProGuard) รวมถึงเวอร์ชันที่ต้องการโดยเฉพาะ ช่วงเวลานี้ ช่วยให้นักพัฒนาไลบรารีปรับแต่งกฎเพื่อให้ทำงานในโปรเจ็กต์ต่างๆ ได้อย่างมีประสิทธิภาพที่สุด ซึ่งใช้เวอร์ชันใหม่ที่เล็กลง ขณะที่อนุญาตให้กฎที่มีอยู่ยังคง ที่ใช้ในโปรเจ็กต์ที่ลดขนาดลง
นักพัฒนาไลบรารีจะต้องใส่กฎการย่อขนาดที่กำหนดเป้าหมายไว้เพื่อระบุ ที่ตำแหน่งที่เจาะจงภายในไลบรารี AAR หรือ JAR ตามที่อธิบายไว้ด้านล่าง
In an AAR library:
proguard.txt (legacy location)
classes.jar
└── META-INF
└── com.android.tools (targeted shrink rules location)
├── r8-from-<X>-upto-<Y>/<R8-rules-file>
└── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>
In a JAR library:
META-INF
├── proguard/<ProGuard-rules-file> (legacy location)
└── com.android.tools (targeted shrink rules location)
├── r8-from-<X>-upto-<Y>/<R8-rules-file>
└── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>
ซึ่งหมายความว่าระบบจะจัดเก็บกฎการย่อขนาดที่กำหนดเป้าหมายไว้ใน META-INF/com.android.tools
ไดเรกทอรีของ JAR หรือในไดเรกทอรี META-INF/com.android.tools
ภายใน
classes.jar
ของ AAR
ในไดเรกทอรีดังกล่าว อาจมีหลายไดเรกทอรีที่มีชื่อในแบบฟอร์ม
ของ r8-from-<X>-upto-<Y>
หรือ proguard-from-<X>-upto-<Y>
เพื่อระบุว่ารายการใด
เวอร์ชันที่มีการเขียนกฎภายในไดเรกทอรีให้เล็กลง
โปรดทราบว่าส่วน -from-<X>
และ -upto-<Y>
นั้นไม่บังคับ แต่เวอร์ชัน <Y>
เป็นแบบไม่รวมและช่วงเวอร์ชันต้องต่อเนื่องกัน
เช่น r8-upto-8.0.0
, r8-from-8.0.0-upto-8.2.0
และ r8-from-8.2.0
สร้างชุดกฎการย่อเป้าหมายที่ถูกต้อง กฎภายใต้
R8 จะใช้ไดเรกทอรี r8-from-8.0.0-upto-8.2.0
ตั้งแต่เวอร์ชัน 8.0.0 ถึง
แต่ไม่รวมเวอร์ชัน 8.2.0
จากข้อมูลดังกล่าว ปลั๊กอิน Android Gradle 3.6 ขึ้นไปจะเลือก
จากไดเรกทอรี R8 ที่ตรงกัน หากไลบรารีไม่ได้ระบุการกำหนดเป้าหมาย
ปลั๊กอิน Android Gradle จะเลือกกฎจาก
สถานที่ตั้ง (proguard.txt
สำหรับ AAR หรือ
META-INF/proguard/<ProGuard-rules-file>
สำหรับ JAR)
นักพัฒนาห้องสมุดสามารถเลือกที่จะรวมกฎการย่อขนาดที่กำหนดเป้าหมายหรือ กฎ ProGuard ในไลบรารี หรือทั้ง 2 ประเภทหากต้องการเก็บ ความเข้ากันได้กับปลั๊กอิน Android Gradle เวอร์ชันเก่ากว่า 3.6 หรือเครื่องมืออื่นๆ
รวมการกำหนดค่าเพิ่มเติม
เมื่อคุณสร้างโปรเจ็กต์หรือโมดูลใหม่โดยใช้ Android Studio IDE จะสร้าง
<module-dir>/proguard-rules.pro
ไฟล์ให้คุณใส่กฎของคุณเอง คุณ
ยังสามารถรวมกฎเพิ่มเติมจากไฟล์อื่นๆ ได้โดยเพิ่มลงใน
พร็อพเพอร์ตี้ proguardFiles
ในสคริปต์บิลด์ของโมดูล
ตัวอย่างเช่น คุณสามารถเพิ่มกฎเฉพาะสำหรับตัวแปรของบิลด์แต่ละรายการได้โดยการเพิ่ม
พร็อพเพอร์ตี้ proguardFiles
อีกรายการหนึ่งในบล็อก productFlavor
ที่เกี่ยวข้อง
ไฟล์ Gradle ต่อไปนี้เพิ่ม flavor2-rules.pro
ลงในเวอร์ชันผลิตภัณฑ์ flavor2
ตอนนี้ flavor2
ใช้กฎ ProGuard ทั้ง 3 ข้อเนื่องจากกฎจาก release
ที่เกี่ยวข้องด้วย
นอกจากนี้ คุณสามารถเพิ่มพร็อพเพอร์ตี้ testProguardFiles
ซึ่งระบุ
รายการไฟล์ ProGuard ที่รวมอยู่ใน APK ทดสอบเท่านั้น:
Kotlin
android { ... buildTypes { getByName("release") { isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), // List additional ProGuard rules for the given build type here. By default, // Android Studio creates and includes an empty rules file for you (located // at the root directory of each module). "proguard-rules.pro" ) testProguardFiles( // The proguard files listed here are included in the // test APK only. "test-proguard-rules.pro" ) } } flavorDimensions.add("version") productFlavors { create("flavor1") { ... } create("flavor2") { proguardFile("flavor2-rules.pro") } } }
ดึงดูด
android { ... buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), // List additional ProGuard rules for the given build type here. By default, // Android Studio creates and includes an empty rules file for you (located // at the root directory of each module). 'proguard-rules.pro' testProguardFiles // The proguard files listed here are included in the // test APK only. 'test-proguard-rules.pro' } } flavorDimensions "version" productFlavors { flavor1 { ... } flavor2 { proguardFile 'flavor2-rules.pro' } } }
ย่อโค้ดของคุณ
ระบบจะเปิดใช้การย่อโค้ดด้วย R8 โดยค่าเริ่มต้นเมื่อคุณตั้งค่าminifyEnabled
พร็อพเพอร์ตี้ไปยัง true
การย่อโค้ด (หรือที่เรียกว่าการสั่นสะเทือนของต้นไม้) เป็นขั้นตอนการนำโค้ดออก ที่ R8 ระบุว่าไม่จำเป็นในระหว่างรันไทม์ ขั้นตอนนี้อาจช่วยลด ขนาดของแอป ตัวอย่างเช่น แอปของคุณมีทรัพยากร Dependency ของไลบรารีจํานวนมากแต่ ใช้ฟังก์ชันการทำงานเพียงเล็กน้อย
หากต้องการย่อโค้ดของแอป R8 จะกำหนดจุดแรกเข้าทั้งหมดลงในโค้ดของแอปก่อน โค้ดตามชุดไฟล์การกำหนดค่าแบบรวม จุดแรกเข้าเหล่านี้ประกอบด้วยชั้นเรียนทั้งหมดที่แพลตฟอร์ม Android อาจใช้เพื่อเปิด กิจกรรมหรือบริการของแอป R8 จะตรวจสอบจากจุดแรกเข้าแต่ละจุด โค้ดของแอปเพื่อสร้างกราฟของวิธีการทั้งหมด ตัวแปรสมาชิก และอื่นๆ คลาสที่แอปของคุณอาจเข้าถึงขณะรันไทม์ รหัสที่ไม่ได้เชื่อมต่อกับ กราฟนั้นถือว่าเข้าถึงไม่ได้และอาจถูกนำออกจากแอป
รูปที่ 1 แสดงแอปที่มีทรัพยากร Dependency ของไลบรารีรันไทม์ ขณะตรวจสอบ
โค้ดของแอป R8 จะกำหนดเมธอด foo()
, faz()
และ bar()
เข้าถึงได้จากจุดแรกเข้า MainActivity.class
อย่างไรก็ตาม คลาส
แอปของคุณจะไม่ใช้ OkayApi.class
หรือเมธอด baz()
ระหว่างรันไทม์ และ
R8 จะนำโค้ดดังกล่าวออกเมื่อย่อแอป
รูปที่ 1 เวลาคอมไพล์ R8 จะสร้าง โดยอิงตามกฎ Keep ที่รวมกันของโปรเจ็กต์เพื่อระบุโค้ดที่เข้าถึงไม่ได้
R8 กำหนดจุดแรกเข้าผ่านกฎ -keep
ใน
ไฟล์การกำหนดค่า R8 กล่าวคือ ให้กฎระบุ
คลาสที่ R8 ไม่ควรทิ้งเมื่อย่อขนาดแอป และ R8 จะพิจารณา
ชั้นเรียนเหล่านั้นให้เป็นจุดแรกเข้าที่แอปอาจช่วยได้ ปลั๊กอิน Android Gradle
และ AAPT2 จะสร้างกฎ Keep ที่แอปส่วนใหญ่จำเป็นต้องใช้โดยอัตโนมัติ
โปรเจ็กต์ให้คุณ เช่น กิจกรรม ยอดดู และบริการของแอป อย่างไรก็ตาม
หากต้องการปรับแต่งลักษณะการทำงานเริ่มต้นนี้ด้วยกฎ Keep เพิ่มเติม โปรดอ่าน
ในส่วนเกี่ยวกับวิธีปรับแต่งโค้ดที่จะเก็บไว้
แต่หากคุณสนใจแค่ลดขนาดทรัพยากรของแอป โปรดข้ามไปยังส่วนเกี่ยวกับวิธีย่อทรัพยากร
โปรดทราบว่าถ้าโปรเจ็กต์ห้องสมุดถูกย่อลง แอปที่ต้องใช้ไลบรารีนั้น รวมคลาสไลบรารีที่ย่อลง คุณอาจต้องปรับกฎ Keep ของไลบรารีในกรณีต่อไปนี้ ไม่มีคลาสใน APK ของไลบรารี หากคุณกำลังสร้างและเผยแพร่ ไลบรารีในรูปแบบ AAR ไฟล์ JAR ในเครื่องที่ไลบรารีของคุณต้องใช้ไม่ใช่ ในไฟล์ AAR น้อยลง
ปรับแต่งรหัสที่จะเก็บไว้
ในสถานการณ์ส่วนใหญ่ ไฟล์กฎ ProGuard เริ่มต้น (proguard-android-optimize.txt
)
ก็เพียงพอสำหรับ R8 ที่สามารถนำออกเฉพาะโค้ดที่ไม่ได้ใช้งาน อย่างไรก็ตาม
บางสถานการณ์ยากที่ R8 จะวิเคราะห์ได้อย่างถูกต้อง และอาจทำให้
กับแอปของคุณได้จริงๆ ตัวอย่างกรณีที่เนื้อหาอาจถูกนำออกอย่างไม่ถูกต้อง
โค้ดได้แก่
- เมื่อแอปเรียกใช้เมธอดจาก Java Native Interface (JNI)
- เมื่อแอปค้นหาโค้ดขณะรันไทม์ (เช่น ขณะมีเงาสะท้อน)
การทดสอบแอปควรเผยให้เห็นข้อผิดพลาดที่เกิดจากการนำแอปออกอย่างไม่เหมาะสม เท่านั้นได้แต่ยังสามารถตรวจสอบรหัสที่นำออกได้โดย สร้างรายงานโค้ดที่นำออก
ในการแก้ไขข้อผิดพลาดและบังคับให้ R8 เก็บโค้ดบางรายการไว้ ให้เพิ่ม
-keep
ในไฟล์กฎ ProGuard เช่น
-keep public class MyClass
อีกทางเลือกหนึ่งคือ คุณสามารถเพิ่ม
@Keep
ไปยังโค้ดที่คุณ
ต้องการเก็บไว้ การเพิ่ม @Keep
ในชั้นเรียนจะทำให้ทั้งชั้นเรียนเป็นอย่างเดิม
การเพิ่ม URL ลงในเมธอดหรือฟิลด์จะเป็นการเก็บเมธอด/ฟิลด์ (และชื่อของฟิลด์) ไว้ด้วย
เป็นชื่อชั้นเรียนเหมือนเดิม โปรดทราบว่าคำอธิบายประกอบนี้ใช้ได้เฉพาะเมื่อใช้
เวลา
ไลบรารีคำอธิบายประกอบของ AndroidX
และเมื่อคุณรวมไฟล์กฎ ProGuard ที่รวมมากับ Android
ปลั๊กอิน Gradle ตามที่อธิบายไว้ในส่วนเกี่ยวกับวิธี
เปิดใช้การย่อขนาด
คุณควรคำนึงถึงสิ่งต่อไปนี้เมื่อใช้ตัวเลือก -keep
สำหรับ
ข้อมูลเพิ่มเติมเกี่ยวกับการปรับแต่งไฟล์กฎของคุณ โปรดอ่าน
คู่มือ ProGuard
การแก้ปัญหา
ส่วนนี้จะระบุปัญหาทั่วไปอื่นๆ ที่คุณอาจพบเมื่อโค้ดของคุณได้รับ
ถูกถอดออก
ตัดไลบรารีเนทีฟ
โดยค่าเริ่มต้น ไลบรารีโค้ดแบบเนทีฟจะถูกนําออกในบิลด์รุ่นของแอป การตัดนี้ประกอบด้วยการนำตารางสัญลักษณ์และข้อมูลการแก้ไขข้อบกพร่องออก ที่อยู่ในไลบรารีเนทีฟที่แอปของคุณใช้ กำลังตัดโค้ดเนทีฟ ไลบรารีช่วยให้ลดขนาดลงได้อย่างมาก อย่างไรก็ตาม ก็เป็นไปไม่ได้ที่จะวินิจฉัย ใน Google Play Console เนื่องจากไม่มีข้อมูล (เช่น ชื่อคลาสและฟังก์ชัน)
การสนับสนุนข้อขัดข้องของระบบ
Google Play Console รายงานข้อขัดข้องของระบบใน Android Vitals ไม่กี่รายการ คุณจะสามารถสร้างและอัปโหลดไฟล์สัญลักษณ์สำหรับแก้ไขข้อบกพร่องของระบบสำหรับแอปของคุณ ไฟล์นี้จะเปิดใช้สแต็กเทรซข้อขัดข้องของระบบซึ่งแทนที่ด้วยสัญลักษณ์ (ซึ่งรวมถึงคลาสและ ชื่อฟังก์ชัน) ใน Android Vitals เพื่อช่วยคุณแก้ไขข้อบกพร่องของแอปในเวอร์ชันที่ใช้งานจริง ขั้นตอนเหล่านี้จะแตกต่างกันไปตามเวอร์ชันของปลั๊กอิน Android Gradle ที่ใช้ใน และเอาต์พุตบิลด์ของโปรเจ็กต์
ปลั๊กอิน Android Gradle เวอร์ชัน 4.1 ขึ้นไป
หากเป็นโปรเจ็กต์สร้าง Android App Bundle คุณจะรวมองค์ประกอบ
ในไฟล์สัญลักษณ์การแก้ไขข้อบกพร่องของระบบ หากต้องการรวมไฟล์นี้ไว้ในบิลด์รุ่น ให้เพิ่ม
ต่อไปนี้กับไฟล์ build.gradle.kts
ของแอป
android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }
เลือกระดับสัญลักษณ์การแก้ไขข้อบกพร่องจากรายการต่อไปนี้
- ใช้
SYMBOL_TABLE
เพื่อดูชื่อฟังก์ชันในสแต็กเทรซที่แทนที่ด้วยสัญลักษณ์ของ Play Console ระดับนี้รองรับ Tombstone - ใช้
FULL
เพื่อดูชื่อ ไฟล์ และหมายเลขบรรทัดของฟังก์ชันใน Play Console สแต็กเทรซที่แทนที่ด้วยสัญลักษณ์
หากโปรเจ็กต์ของคุณสร้าง APK ให้ใช้การตั้งค่าบิลด์ build.gradle.kts
ที่แสดงอยู่
ก่อนหน้านี้เพื่อสร้างไฟล์สัญลักษณ์การแก้ไขข้อบกพร่องของระบบแยกต่างหาก สร้างด้วยตนเอง
อัปโหลดไฟล์สัญลักษณ์การแก้ไขข้อบกพร่องของระบบ
ใน Google Play Console ในฐานะส่วนหนึ่งของกระบวนการบิลด์ Android Gradle
ปลั๊กอินจะส่งออกไฟล์นี้ในตำแหน่งโปรเจ็กต์ต่อไปนี้
app/build/outputs/native-debug-symbols/variant-name/native-debug-symbols.zip
ปลั๊กอิน Android Gradle เวอร์ชัน 4.0 หรือเก่ากว่า (และระบบบิลด์อื่นๆ)
ในกระบวนการบิลด์ ปลั๊กอิน Android Gradle จะเก็บสำเนาของไฟล์ ไลบรารีที่ยังไม่ได้ Strip ในไดเรกทอรีโปรเจ็กต์ โครงสร้างไดเรกทอรีนี้มีลักษณะคล้ายกับด้านล่างนี้
app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
├── arm64-v8a/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
├── x86/
│ ├── libgameengine.so
│ ├── libothercode.so
│ └── libvideocodec.so
└── x86_64/
├── libgameengine.so
├── libothercode.so
└── libvideocodec.so
บีบอัดเนื้อหาของไดเรกทอรีนี้:
cd app/build/intermediates/cmake/universal/release/obj
zip -r symbols.zip .
สร้างด้วยตนเอง อัปโหลดไฟล์
symbols.zip
ใน Google Play Console
ลดทรัพยากร
การย่อทรัพยากรทำงานร่วมกับการย่อโค้ดเท่านั้น หลังจาก ตัวลดขนาดโค้ดจะนำโค้ดที่ไม่ได้ใช้งานทั้งหมดออก ตัวย่อทรัพยากรสามารถระบุได้ ที่แอปยังใช้อยู่ โดยเฉพาะอย่างยิ่งเมื่อคุณเพิ่มโค้ด ไลบรารีที่มีทรัพยากร คุณต้องนำโค้ดไลบรารีที่ไม่ได้ใช้ออกเพื่อให้ ทรัพยากรห้องสมุดจะไม่มีการอ้างอิง และด้วยเหตุนี้ ทรัพยากรจึงนำออกได้ เครื่องมือย่อขนาด
หากต้องการเปิดใช้การย่อทรัพยากร ให้ตั้งค่าพร็อพเพอร์ตี้ shrinkResources
ไปยัง true
ในสคริปต์บิลด์ (พร้อมกับ
minifyEnabled
สำหรับการย่อโค้ด) เช่น
Kotlin
android { ... buildTypes { getByName("release") { isShrinkResources = true isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" ) } } }
ดึงดูด
android { ... buildTypes { release { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
หากคุณยังไม่ได้สร้างแอปโดยใช้ minifyEnabled
สำหรับ
การลดขนาดโค้ด แล้วลองทำก่อนเปิดใช้ shrinkResources
เพราะคุณอาจต้องแก้ไขไฟล์ proguard-rules.pro
เป็น
เก็บคลาสหรือเมธอดที่สร้างหรือเรียกใช้แบบไดนามิกก่อนที่คุณจะ
เริ่มนำแหล่งข้อมูลออก
ปรับแต่งทรัพยากรที่จะเก็บไว้
หากมีทรัพยากรเฉพาะที่ต้องการเก็บหรือทิ้ง ให้สร้าง XML
ในโปรเจ็กต์ด้วยแท็ก <resources>
และระบุแต่ละรายการ
ทรัพยากรที่จะเก็บไว้ในแอตทริบิวต์ tools:keep
และทรัพยากรแต่ละรายการ
ทิ้งในแอตทริบิวต์ tools:discard
แอตทริบิวต์ทั้งสองยอมรับ
รายชื่อทรัพยากรที่คั่นด้วยคอมมา คุณสามารถใช้อักขระดอกจันเป็น
ไวลด์การ์ด
เช่น
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" tools:discard="@layout/unused2" />
บันทึกไฟล์นี้ในทรัพยากรของโปรเจ็กต์ เช่น ที่
res/raw/my.package.keep.xml
บิลด์ไม่ได้รวมไฟล์นี้ลงใน
แอป
หมายเหตุ: ตรวจสอบว่าคุณใช้ชื่อที่ไม่ซ้ำกันสำหรับไฟล์ keep
วันและเวลา
ไลบรารีต่างๆ จะลิงก์กัน กฎ Keep เหล่านั้นจะขัดแย้งกัน
ไม่เช่นนั้นจะทำให้เกิดปัญหาที่อาจเกิดขึ้นกับกฎที่ไม่สนใจหรือเก็บไว้โดยไม่จำเป็น
ที่ไม่ซับซ้อน
การระบุทรัพยากรที่จะทิ้งอาจดูแปลกๆ ทั้งที่ถ้าทำได้
ให้ลบออกแทน แต่วิธีนี้อาจเป็นประโยชน์เมื่อใช้ตัวแปรของบิลด์ สำหรับ
เช่น คุณอาจใส่ทรัพยากรทั้งหมด
ลงในไดเรกทอรีโปรเจ็กต์ทั่วไป
ให้สร้างไฟล์ my.package.build.variant.keep.xml
แยกกันสำหรับแต่ละไฟล์
ตัวแปรบิลด์เมื่อทราบว่าทรัพยากรที่ระบุดูเหมือนจะมีการใช้ในโค้ด
(ซึ่งจะทำให้เครื่องมือย่อโฆษณาไม่ได้เอาออก) แต่คุณรู้ว่าจะไม่ทำให้
ที่ใช้สำหรับตัวแปรของบิลด์ที่กำหนด นอกจากนี้ยังเป็นไปได้ว่าเครื่องมือบิลด์
ระบุทรัพยากรตามที่ต้องการได้ไม่ถูกต้อง ซึ่งอาจเป็นเพราะ
คอมไพเลอร์จะเพิ่มรหัสทรัพยากรในบรรทัด จากนั้นเครื่องมือวิเคราะห์ทรัพยากรอาจไม่
ทราบความแตกต่างระหว่างแหล่งข้อมูลที่อ้างอิงตามจริงกับค่าจำนวนเต็ม
ในโค้ดที่เกิดขึ้นให้มีค่าเท่ากัน
เปิดใช้การตรวจสอบข้อมูลอ้างอิงที่เข้มงวด
โดยปกติแล้ว เครื่องมือย่อทรัพยากรจะกำหนดได้อย่างแม่นยำว่าทรัพยากร
อย่างไรก็ตาม หากรหัสของคุณเรียกใช้
Resources.getIdentifier()
(หรือหากมีไลบรารีของคุณดำเนินการดังกล่าว นั่นก็คือ AppCompat
แสดงว่าโค้ดของคุณกำลังค้นหาชื่อทรัพยากรตาม
สตริงที่สร้างแบบไดนามิก เมื่อทำเช่นนี้ ตัวลดขนาดทรัพยากรจะทำงาน
ป้องกันตามค่าเริ่มต้นและทำเครื่องหมายทรัพยากรทั้งหมดที่มีรูปแบบชื่อที่ตรงกันเป็น
อาจมีนำไปใช้และนำออกไม่ได้
ตัวอย่างเช่น โค้ดต่อไปนี้จะทําให้ทรัพยากรทั้งหมดที่มี
คำนำหน้า img_
ที่จะทำเครื่องหมายว่าใช้แล้ว
Kotlin
val name = String.format("img_%1d", angle + 1) val res = resources.getIdentifier(name, "drawable", packageName)
Java
String name = String.format("img_%1d", angle + 1); res = getResources().getIdentifier(name, "drawable", getPackageName());
ตัวย่อทรัพยากรยังพิจารณาค่าคงที่สตริงทั้งหมดใน
รวมถึงทรัพยากรต่างๆ ของ res/raw/
เพื่อมองหาทรัพยากร
URL ในรูปแบบที่คล้ายกับ
file:///android_res/drawable//ic_plus_anim_016.png
หากพบ
สตริงลักษณะนี้หรืออื่นๆ ที่ดูเหมือนสามารถใช้ในการสร้าง URL ได้
แบบนี้ จะไม่นำผู้ชมเหล่านั้นออก
ตัวอย่างโหมดย่อภาพที่ปลอดภัยซึ่งเปิดใช้อยู่โดยค่าเริ่มต้นมีดังนี้
อย่างไรก็ตาม คุณสามารถปิดฟีเจอร์ "ปลอดภัยดีกว่าขอโทษ" นี้ การจัดการ และระบุ
เครื่องมือย่อทรัพยากรจะเก็บเฉพาะทรัพยากรที่มีการใช้แล้ว ถึง
ตั้งค่า shrinkMode
เป็น strict
ในส่วน
keep.xml
ดังต่อไปนี้
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:shrinkMode="strict" />
หากคุณเปิดใช้โหมดการย่อขนาดที่เข้มงวดและโค้ดของคุณอ้างอิงถึง
ด้วยสตริงที่สร้างขึ้นแบบไดนามิก ดังที่แสดงด้านบน คุณต้อง
เก็บทรัพยากรเหล่านั้นด้วยตนเองโดยใช้แอตทริบิวต์ tools:keep
นำทรัพยากรอื่นที่ไม่ได้ใช้ออก
เครื่องมือย่อทรัพยากร Gradle จะนำเฉพาะทรัพยากรที่ไม่ได้อ้างอิงออกเท่านั้น
ตามรหัสแอปของคุณ ซึ่งหมายความว่าจะไม่มีการนำ ออก
ทรัพยากรอื่นสำหรับการกำหนดค่าอุปกรณ์แบบต่างๆ หากจำเป็น
คุณสามารถใช้พร็อพเพอร์ตี้ resConfigs
ของปลั๊กอิน Android Gradle เพื่อ
นำไฟล์ทรัพยากรสำรองที่แอปของคุณไม่จำเป็นต้องใช้ออก
ตัวอย่างเช่น หากคุณใช้ไลบรารีที่มีทรัพยากรด้านภาษา
(เช่น AppCompat หรือบริการ Google Play) แอปของคุณจะรวม
สตริงภาษาที่แปลสำหรับข้อความในไลบรารีเหล่านั้นว่า
ส่วนแอปที่เหลือจะได้รับการแปลเป็นภาษาเดียวกันหรือไม่ หากคุณต้องการ
เก็บไว้เฉพาะภาษาที่แอปของคุณรองรับอย่างเป็นทางการเท่านั้น คุณสามารถระบุ
ภาษาเหล่านั้นโดยใช้พร็อพเพอร์ตี้ resConfig
ทรัพยากรสำหรับ
ภาษาที่ไม่ได้ระบุจะถูกลบออก
ตัวอย่างต่อไปนี้แสดงวิธีจำกัดทรัพยากรทางภาษาให้เหลือเพียง ภาษาอังกฤษและฝรั่งเศส
Kotlin
android { defaultConfig { ... resourceConfigurations.addAll(listOf("en", "fr")) } }
ดึงดูด
android { defaultConfig { ... resConfigs "en", "fr" } }
เมื่อเผยแพร่แอปโดยใช้รูปแบบ Android App Bundle โดยค่าเริ่มต้นเท่านั้น ระบบจะดาวน์โหลดภาษาที่กำหนดค่าไว้ในอุปกรณ์ของผู้ใช้เมื่อติดตั้งแอป ในทำนองเดียวกัน เฉพาะทรัพยากรที่ตรงกับความหนาแน่นของหน้าจอของอุปกรณ์และเนทีฟ ไลบรารีที่ตรงกับ ABI ของอุปกรณ์จะรวมอยู่ในการดาวน์โหลดด้วย สำหรับข้อมูลเพิ่มเติม อ้างอิงถึง แอป Android การกำหนดค่า Bundle
สำหรับแอปเดิมที่เปิดตัวด้วย APK (สร้างก่อนเดือนสิงหาคม 2021) คุณสามารถ ปรับแต่งความหนาแน่นของหน้าจอหรือทรัพยากร ABI ที่จะใส่ใน APK โดย การสร้าง APK หลายรายการ ว่าแต่ละเป้าหมายมีการกำหนดค่าอุปกรณ์แตกต่างกัน
ผสานทรัพยากรที่ซ้ำกัน
โดยค่าเริ่มต้น Gradle จะรวมทรัพยากรที่มีชื่อเหมือนกันด้วย เช่น
ไฟล์ที่ถอนออกได้ซึ่งมีชื่อเดียวกันซึ่งอาจอยู่ในโฟลเดอร์ทรัพยากรต่างกัน ช่วงเวลานี้
พร็อพเพอร์ตี้ shrinkResources
ไม่ได้ควบคุมพฤติกรรม และ
ไม่สามารถปิดใช้งานได้ เนื่องจากจำเป็นต้องหลีกเลี่ยงข้อผิดพลาดเมื่อ
ทรัพยากรตรงกับชื่อที่คุณกำลังค้นหา
การรวมทรัพยากรจะเกิดขึ้นก็ต่อเมื่อไฟล์อย่างน้อย 2 ไฟล์ใช้ไฟล์เดียวกัน ชื่อ ประเภท และตัวระบุทรัพยากร Gradle เลือกไฟล์ที่จะพิจารณา เป็นตัวเลือกที่ดีที่สุดจากรายชื่อที่ซ้ำกัน (ตามลำดับความสำคัญที่อธิบาย ด้านล่าง) และส่งต่อทรัพยากรดังกล่าวเพียง 1 รายการไปยัง AAPT เพื่อแจกจ่ายใน สิ่งประดิษฐ์ขั้นสุดท้าย
Gradle มองหาทรัพยากรที่ซ้ำกันในตำแหน่งต่อไปนี้
- ทรัพยากรหลัก ซึ่งเชื่อมโยงกับชุดแหล่งที่มาหลัก โดยทั่วไป
อยู่ใน
src/main/res/
- การวางซ้อนตัวแปรจากประเภทบิลด์และเวอร์ชันบิลด์
- ทรัพยากร Dependency ของโปรเจ็กต์ไลบรารี
Gradle รวมทรัพยากรที่ซ้ำกันตามลำดับความสำคัญแบบต่อเรียงต่อไปนี้
ทรัพยากร Dependency → หลัก → เวอร์ชันบิลด์ → ประเภทบิลด์
เช่น หากทรัพยากรที่ซ้ำกันปรากฏขึ้นทั้งในทรัพยากรหลักและ เวอร์ชันบิลด์ Gradle จะเลือกเวอร์ชันในเวอร์ชันบิลด์
หากทรัพยากรที่เหมือนกันปรากฏในชุดแหล่งที่มาเดียวกัน Gradle จะรวมไม่ได้
และแสดงข้อผิดพลาดในการรวมทรัพยากร กรณีนี้อาจเกิดขึ้นได้หากคุณกำหนด
ชุดแหล่งที่มาในพร็อพเพอร์ตี้ sourceSet
ของ
build.gradle.kts
- ตัวอย่างเช่น หากทั้ง src/main/res/
และ src/main/res2/
มีทรัพยากรที่เหมือนกัน
ปรับโค้ดให้ยากต่อการอ่าน (Obfuscate)
วัตถุประสงค์ของการสร้างความสับสนคือการลดขนาดแอปโดยการย่อชื่อของ คลาส วิธีการ และฟิลด์ต่างๆ ของแอป ต่อไปนี้เป็นตัวอย่างของ การสร้างความสับสนโดยใช้ R8
androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
android.content.Context mContext -> a
int mListItemLayout -> O
int mViewSpacingRight -> l
android.widget.Button mButtonNeutral -> w
int mMultiChoiceItemLayout -> M
boolean mShowTitle -> P
int mViewSpacingLeft -> j
int mButtonPanelSideLayout -> K
แม้ว่าการปรับให้ยากต่อการอ่าน (Obfuscation) จะไม่นำโค้ดออกจากแอป แต่ช่วยลดขนาดได้อย่างมาก จะเห็นในแอปที่มีไฟล์ DEX ที่จัดทำดัชนีชั้นเรียน เมธอด และฟิลด์ต่างๆ อย่างไรก็ตาม เมื่อการปรับให้ยากต่อการอ่าน (Obfuscation) เปลี่ยนชื่อส่วนต่างๆ ของโค้ด งานบางอย่าง เช่น การตรวจสอบสแต็กเทรซ จะต้องใช้เครื่องมือเพิ่มเติม หากต้องการทำความเข้าใจ สแต็กเทรซหลังจากการสร้างความสับสน ให้อ่านหัวข้อเกี่ยวกับวิธี ถอดรหัสสแต็กเทรซที่ปรับให้ยากต่อการอ่าน (Obfuscate)
นอกจากนี้ หากโค้ดของคุณต้องใช้การตั้งชื่อที่คาดเดาได้สำหรับเมธอดของแอป และคลาส ตัวอย่างเช่น เมื่อใช้การสะท้อนความรู้สึก คุณควรปฏิบัติกับ เป็นจุดแรกเข้าและระบุกฎ Keep สำหรับแท็ก ตามที่อธิบายไว้ใน เกี่ยวกับวิธีปรับแต่งโค้ดที่จะเก็บไว้ นั่นคือการเก็บ กฎจะบอกให้ R8 ไม่เพียงเก็บโค้ดนั้นไว้ใน DEX สุดท้ายของแอป แต่ให้เก็บโค้ดนั้นไว้ ชื่อดั้งเดิม
ถอดรหัสสแต็กเทรซที่ปรับให้ยากต่อการอ่าน (Obfuscate)
หลังจาก R8 ทำให้โค้ดของคุณยากต่อการอ่าน (Obfuscate) แล้ว การทำความเข้าใจสแต็กเทรซจะเป็นเรื่องยาก (หากทำไม่ได้) เนื่องจากชื่อคลาสและเมธอดอาจมีการ มีการเปลี่ยนแปลง หากต้องการรับสแต็กเทรซต้นฉบับ คุณควร ย้อนรอยสแต็กเทรซ
การเพิ่มประสิทธิภาพโค้ด
เพื่อเป็นการเพิ่มประสิทธิภาพแอปของคุณให้มากยิ่งขึ้น R8 จะตรวจสอบโค้ดของคุณอย่างละเอียดยิ่งขึ้น เพื่อลบโค้ดที่ไม่ได้ใช้งานมากขึ้น หรือเขียนโค้ดใหม่หากเป็นไปได้ มีรายละเอียดน้อยลง ตัวอย่างการเพิ่มประสิทธิภาพดังกล่าวมีดังต่อไปนี้
- หากรหัสของคุณไม่ได้ใช้ Branch ของ
else {}
สําหรับคำสั่ง if/else ที่ระบุ R8 อาจนำรหัสของสาขาelse {}
ออก - หากโค้ดเรียกใช้เมธอดในที่เพียงไม่กี่แห่ง R8 อาจนำเมธอดออก และแทรกในบรรทัดในเว็บไซต์สำหรับการโทร 2-3 แห่ง
- หาก R8 ระบุว่าคลาสมีคลาสย่อยที่ไม่ซ้ำกันเพียงรายการเดียว คลาส ไม่มีการสร้างอินสแตนซ์ (เช่น คลาสพื้นฐานนามธรรมที่ใช้โดย ชั้นเรียนการปรับใช้ที่เป็นรูปธรรมหนึ่งชั้น) จากนั้น R8 จะสามารถรวมทั้งสองชั้นเรียนเข้าด้วยกันและ นำชั้นเรียนออกจากแอป
- หากต้องการดูข้อมูลเพิ่มเติม โปรดอ่าน บล็อกโพสต์การเพิ่มประสิทธิภาพ R8 โดย Jake Wharton
R8 ไม่อนุญาตให้ปิดใช้งานหรือเปิดใช้การเพิ่มประสิทธิภาพแบบแยกกัน หรือแก้ไข
ของการเพิ่มประสิทธิภาพ ที่จริงแล้ว R8 ไม่สนใจกฎ ProGuard ใดๆ ที่พยายาม
เพื่อแก้ไขการเพิ่มประสิทธิภาพเริ่มต้น เช่น -optimizations
และ
-optimizationpasses
ข้อจำกัดนี้มีความสำคัญเนื่องจาก เนื่องจาก R8 ยังคง
การปรับปรุง การรักษาลักษณะการทำงานมาตรฐานสำหรับการเพิ่มประสิทธิภาพช่วยให้ Android
ทีม Studio สามารถแก้ปัญหาที่คุณอาจพบได้อย่างง่ายดาย
โปรดทราบว่าการเปิดใช้การเพิ่มประสิทธิภาพจะเปลี่ยนสแต็กเทรซ แอปพลิเคชัน เช่น ในบรรทัดจะนำสแต็กเฟรมออก ดูหัวข้อนี้ใน retracing เพื่อดูวิธีรับสแต็กเทรซต้นฉบับ
ผลกระทบต่อประสิทธิภาพของรันไทม์
หากเปิดใช้การย่อเนื้อหา การสร้างความสับสน และการเพิ่มประสิทธิภาพทั้งหมด R8 จะปรับปรุง ประสิทธิภาพรันไทม์ของโค้ด (รวมถึงเวลาเริ่มต้นและเวลาที่ใช้ในการแสดงผลเฟรมบนเธรด UI) ได้สูงสุดถึง 30% การปิดใช้การตั้งค่าเหล่านี้จะจำกัดชุดการเพิ่มประสิทธิภาพเป็นอย่างมาก การใช้งาน R8
หากเปิดใช้ R8 คุณควร สร้างโปรไฟล์สตาร์ทอัพ เพื่อประสิทธิภาพในสตาร์ทอัพที่ดียิ่งขึ้น
เปิดใช้การเพิ่มประสิทธิภาพในเชิงรุกมากขึ้น
R8 มีชุดการเพิ่มประสิทธิภาพเพิ่มเติม (เรียกว่า "โหมดเต็ม") ซึ่ง ทำให้อุปกรณ์ทำงานแตกต่างจาก ProGuard การเพิ่มประสิทธิภาพเหล่านี้เปิดใช้โดย เริ่มต้นตั้งแต่ ปลั๊กอิน Android Gradle เวอร์ชัน 8.0.0
คุณสามารถปิดใช้การเพิ่มประสิทธิภาพเพิ่มเติมเหล่านี้ได้โดยการรวมข้อมูลต่อไปนี้ใน
ไฟล์ gradle.properties
ของโปรเจ็กต์:
android.enableR8.fullMode=false
เนื่องจากการเพิ่มประสิทธิภาพเพิ่มเติมทำให้ R8 ทำงานแตกต่างจาก ProGuard คุณอาจต้องใส่กฎ ProGuard เพิ่มเติมเพื่อหลีกเลี่ยงรันไทม์ หากใช้กฎที่ออกแบบมาเพื่อ ProGuard เช่น สมมติว่า โค้ดที่อ้างอิงคลาสผ่าน Java Reflection API เมื่อไม่ได้ใช้ "โหมดเต็ม" R8 ถือว่าคุณตั้งใจที่จะตรวจสอบและดัดแปลงวัตถุ คลาสดังกล่าวขณะรันไทม์ แม้ว่าโค้ดจะไม่เป็นแบบรันไทม์ก็ตาม และจะทำงานโดยอัตโนมัติ ช่วยเก็บชั้นเรียน และเครื่องมือเริ่มต้นแบบคงที่
อย่างไรก็ตาม เมื่อใช้ "โหมดเต็ม" R8 จะไม่ตั้งสมมติฐานนี้ และถ้า R8 ยืนยันว่าโค้ดของคุณไม่เคยใช้คลาสขณะรันไทม์เลย และจะนำโค้ด จาก DEX สุดท้ายของแอป กล่าวคือ ถ้าคุณต้องการเก็บชั้นเรียนและ เริ่มต้นแบบคงที่ คุณต้องรวมกฎ Keep ไว้ในไฟล์กฎของคุณ ได้
หากพบปัญหาขณะใช้ "โหมดเต็ม" ของ R8 โปรดดู หน้าคำถามที่พบบ่อยเกี่ยวกับ R8 เพื่อหาวิธีแก้ปัญหาที่เป็นไปได้ หากแก้ไขปัญหาไม่ได้ โปรด รายงานข้อบกพร่อง
การติดตามสแต็กเทรซย้อนหลัง
มีการเปลี่ยนแปลงโค้ดที่ประมวลผลโดย R8 ด้วยวิธีต่างๆ ที่สามารถสร้างสแต็กเทรซได้ เข้าใจยากขึ้นเพราะสแต็กเทรซไม่สอดคล้องกับซอร์สโค้ด ช่วงเวลานี้ อาจเป็นกรณีสำหรับการเปลี่ยนแปลงหมายเลขบรรทัดเมื่อข้อมูลการดีบักคือ ไม่ได้เก็บไว้ สาเหตุอาจเกิดจากการเพิ่มประสิทธิภาพ เช่น การใส่ข้อมูลในบรรทัดและการทำโครงร่าง สิ่งที่มากที่สุดคือการสร้างความสับสน ซึ่งแม้แต่ชั้นเรียนและวิธีการต่างๆ ก็จะ เปลี่ยนชื่อ
ในการกู้คืนสแต็กเทรซต้นฉบับ R8 จะจัดเตรียมเมธอด retrace เครื่องมือบรรทัดคำสั่ง ซึ่ง ที่มาพร้อมกับแพ็กเกจเครื่องมือบรรทัดคำสั่ง
หากต้องการรองรับการติดตามสแต็กเทรซของแอปพลิเคชัน คุณควรตรวจสอบว่า
บิลด์จะมีข้อมูลที่เพียงพอสำหรับการติดตามย้อนหลังด้วยการเพิ่มค่าต่อไปนี้
ลงในไฟล์ proguard-rules.pro
ของโมดูล ดังนี้
-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile
แอตทริบิวต์ LineNumberTable
จะเก็บข้อมูลตำแหน่งไว้
ในวิธีการที่ให้ตำแหน่งเหล่านั้นพิมพ์ในสแต็กเทรซ แอตทริบิวต์ SourceFile
เพื่อให้มั่นใจว่ารันไทม์ที่เป็นไปได้ทั้งหมดจะพิมพ์ข้อมูลตำแหน่งได้จริงๆ
คำสั่ง -renamesourcefileattribute
จะตั้งชื่อไฟล์ต้นฉบับในกลุ่ม
ติดตามเพียงแค่ SourceFile
ชื่อไฟล์ต้นฉบับเดิมไม่ใช่
ที่จำเป็นเมื่อติดตาม เนื่องจากไฟล์การแมปมีไฟล์ต้นฉบับเดิม
R8 สร้างไฟล์ mapping.txt
ทุกครั้งที่ทำงาน ซึ่ง
มีข้อมูลที่จำเป็นในการแมปสแต็กเทรซกลับไปยังไฟล์ต้นฉบับ
สแต็กเทรซ Android Studio จะบันทึกไฟล์ไว้ใน
<module-name>/build/outputs/mapping/<build-type>/
ไดเรกทอรี
เมื่อเผยแพร่แอปใน Google Play คุณสามารถอัปโหลดไฟล์ mapping.txt
กับแอปแต่ละเวอร์ชันได้ด้วย เมื่อเผยแพร่โดยใช้ Android App Bundle
รวมโดยอัตโนมัติเป็นส่วนหนึ่งของเนื้อหา App Bundle จากนั้น Google
Play จะติดตามสแต็กเทรซขาเข้าจากปัญหาที่ผู้ใช้รายงาน เพื่อให้คุณ
สามารถตรวจสอบได้ใน Play Console โปรดดูข้อมูลเพิ่มเติมที่ศูนย์ช่วยเหลือ
บทความเกี่ยวกับวิธี
ลดความยุ่งเหยิงของสแต็กเทรซข้อขัดข้อง
แก้ปัญหาด้วย R8
ส่วนนี้จะอธิบายกลยุทธ์บางอย่างในการแก้ปัญหาเมื่อเปิดใช้ การลดขนาด การสร้างความสับสน และการเพิ่มประสิทธิภาพโดยใช้ R8 หากไม่พบวิธีแก้ไขปัญหา เกี่ยวกับปัญหาของคุณด้านล่างนี้ โปรดอ่าน หน้าคำถามที่พบบ่อยเกี่ยวกับ R8 และ คู่มือการแก้ปัญหาของ ProGuard
สร้างรายงานโค้ดที่นำออก (หรือเก็บไว้)
ในการช่วยคุณแก้ปัญหาบางอย่างเกี่ยวกับ R8 การดูรายงานเกี่ยวกับ
โค้ดทั้งหมดที่ R8 นำออกจากแอปของคุณ สำหรับแต่ละโมดูลที่คุณต้องการ
เพื่อสร้างรายงานนี้ ให้เพิ่ม -printusage <output-dir>/usage.txt
ลงใน
ไฟล์กฎ เมื่อคุณเปิดใช้ R8 และสร้างแอปแล้ว R8 จะแสดงผล
ด้วยเส้นทางและชื่อไฟล์ที่คุณระบุ รายงานโค้ดที่นำออก
มีลักษณะคล้ายกับตัวอย่างต่อไปนี้
androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
public boolean hasWindowFeature(int)
public void setHandleNativeActionModesEnabled(boolean)
android.view.ViewGroup getSubDecor()
public void setLocalNightMode(int)
final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
private static final boolean DEBUG
private static final java.lang.String KEY_LOCAL_NIGHT_MODE
static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
...
แต่หากต้องการดูรายงานจุดเข้าที่ R8 ระบุ
กฎ Keep ของโปรเจ็กต์ ให้ใส่ -printseeds <output-dir>/seeds.txt
ไว้ในไฟล์
ไฟล์กฎที่กำหนดเอง เมื่อเปิดใช้ R8 และสร้างแอป เอาต์พุต R8
รายงานที่มีเส้นทางและชื่อไฟล์ที่คุณระบุ รายงานรายการที่เก็บไว้
จุดมีลักษณะคล้ายกับตัวอย่างต่อไปนี้
com.example.myapplication.MainActivity
androidx.appcompat.R$layout: int abc_action_menu_item_layout
androidx.appcompat.R$attr: int activityChooserViewStyle
androidx.appcompat.R$styleable: int MenuItem_android_id
androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
androidx.lifecycle.FullLifecycleObserverAdapter
...
แก้ปัญหาการย่อทรัพยากร
เมื่อคุณย่อทรัพยากร เครื่องมือสร้าง
แสดงข้อมูลสรุปของ
ทรัพยากรที่ถูกนำออกจากแอป (คุณต้องคลิกสลับมุมมองก่อน
ทางด้านซ้ายของหน้าต่างเพื่อแสดงข้อความโดยละเอียดจาก Gradle) เช่น
:android:shrinkDebugResources
Removed unused resources: Resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning
Gradle สร้างไฟล์การวินิจฉัยชื่อ resources.txt
ใน
<module-name>/build/outputs/mapping/release/
(อัตรา
เป็นไฟล์เอาต์พุตของ ProGuard) ไฟล์นี้มีรายละเอียด เช่น
มีการอ้างอิงแหล่งข้อมูลอื่นๆ และแหล่งข้อมูลใดที่มีการใช้ หรือ
ลบแล้ว
เช่น หากต้องการทราบว่าทำไม @drawable/ic_plus_anim_016
จึง
ที่ยังอยู่ในแอป ให้เปิดไฟล์ resources.txt
แล้วค้นหารายการดังกล่าว
ชื่อไฟล์ คุณอาจพบว่ามีการอ้างอิงจากแหล่งข้อมูลอื่น
ดังต่อไปนี้:
16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out] @drawable/ic_plus_anim_016
ตอนนี้คุณอยากทราบว่าทำไม @drawable/add_schedule_fab_icon_anim
เข้าถึงได้ และถ้าค้นหาจากด้านบน คุณจะพบว่ามีรายการทรัพยากรนั้นแสดง
ภายใต้ "ทรัพยากรที่เข้าถึงได้ในระดับรูทคือ" ซึ่งหมายความว่ามีโค้ดที่อ้างอิง
ไปยัง add_schedule_fab_icon_anim
(ซึ่งก็คือรหัส R.drawable คือ
ในรหัสที่เข้าถึงได้)
หากไม่ได้ใช้การตรวจสอบที่เข้มงวด ระบบอาจทำเครื่องหมายรหัสทรัพยากรว่าเข้าถึงได้ หากมีค่าคงที่สตริงที่ดูเหมือนอาจใช้ในการสร้าง ชื่อทรัพยากรสำหรับทรัพยากรที่โหลดแบบไดนามิก ในกรณีนี้ หากคุณค้นหา เอาต์พุตของบิลด์สำหรับชื่อทรัพยากร คุณอาจพบข้อความดังนี้
10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
used because it format-string matches string pool constant ic_plus_anim_%1$d.
หากคุณเห็นสตริงใดสตริงหนึ่งเหล่านี้และมั่นใจว่าไม่ใช่สตริงนั้น
ที่ใช้สำหรับโหลดทรัพยากรที่ระบุแบบไดนามิก คุณสามารถใช้
tools:discard
เพื่อแจ้งระบบบิลด์ให้นำออก
ตามที่อธิบายไว้ในหัวข้อเกี่ยวกับวิธีปรับแต่งทรัพยากรที่จะเก็บไว้